Programing

컨트롤러의 Hibernate / JPA에서 게으른 페치 된 항목을로드하는 방법

crosscheck 2020. 7. 2. 07:53
반응형

컨트롤러의 Hibernate / JPA에서 게으른 페치 된 항목을로드하는 방법


Person 클래스가 있습니다.

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(fetch = FetchType.LAZY)
    private List<Role> roles;
    // etc
}

게으른 다 대다 관계.

내 컨트롤러에는

@Controller
@RequestMapping("/person")
public class PersonController {
    @Autowired
    PersonRepository personRepository;

    @RequestMapping("/get")
    public @ResponseBody Person getPerson() {
        Person person = personRepository.findOne(1L);
        return person;
    }
}

PersonRepository는 이 가이드 에 따라 작성된 코드입니다.

public interface PersonRepository extends JpaRepository<Person, Long> {
}

그러나이 컨트롤러에는 실제로 지연 데이터가 필요합니다. 로딩을 어떻게 트리거 할 수 있습니까?

액세스하려고하면 실패합니다

no.dusken.momus.model.Person.roles 역할 컬렉션을 느리게 초기화하지 못했습니다. 프록시를 초기화 할 수 없습니다. 세션이 없습니다.

또는 내가 시도한 것에 따라 다른 예외.

필요한 경우 xml-description

감사.


초기화를 위해 게으른 컬렉션을 명시 적으로 호출해야합니다 (일반적인 방법은 .size()이 목적 으로 호출 하는 것입니다). Hibernate에는 이것에 대한 전용 메소드가 Hibernate.initialize()있지만 JPA는 이에 상응하는 것이 없다. 물론 세션이 여전히 사용 가능할 때 호출이 완료되었는지 확인해야하므로 컨트롤러 메소드에 주석을 달십시오 @Transactional. 대안은 컨트롤러와 리포지토리간에 중간 서비스 계층을 만들어 지연 수집을 초기화하는 메소드를 노출시킬 수 있습니다.

최신 정보:

위의 솔루션은 쉽지만 데이터베이스에 대한 두 가지 고유 한 쿼리 (사용자 용, 다른 하나는 역할 용)가 발생합니다. 성능을 향상 시키려면 다음 방법을 Spring Data JPA 저장소 인터페이스에 추가하십시오.

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query("SELECT p FROM Person p JOIN FETCH p.roles WHERE p.id = (:id)")
    public Person findByIdAndFetchRolesEagerly(@Param("id") Long id);

}

이 메소드는 JPQL의 페치 조인 절을 사용 하여 단일 라운드의 역할 연관을 데이터베이스에 간결하게로드하므로 위의 솔루션에서 두 가지 고유 한 쿼리로 인해 발생하는 성능 저하를 완화합니다.


이 게시물은 오래된 게시물이지만 @NamedEntityGraph (Javax Persistence) 및 @EntityGraph (Spring Data JPA) 사용을 고려하십시오. 조합이 작동합니다.

@Entity
@Table(name = "Employee", schema = "dbo", catalog = "ARCHO")
@NamedEntityGraph(name = "employeeAuthorities",
            attributeNodes = @NamedAttributeNode("employeeGroups"))
public class EmployeeEntity implements Serializable, UserDetails {
// your props
}

다음과 같이 봄 레포

@RepositoryRestResource(collectionResourceRel = "Employee", path = "Employee")
public interface IEmployeeRepository extends PagingAndSortingRepository<EmployeeEntity, String>           {

    @EntityGraph(value = "employeeAuthorities", type = EntityGraphType.LOAD)
    EmployeeEntity getByUsername(String userName);

}

몇 가지 옵션이 있습니다

  • RJ가 제안한대로 초기화 된 엔티티를 리턴하는 메소드를 저장소에 작성하십시오.

더 많은 작업, 최고의 성능.

  • OpenEntityManagerInViewFilter를 사용하여 전체 요청에 대해 세션을 열린 상태로 유지하십시오.

웹 환경에서는 일반적으로 허용되는 작업이 줄어 듭니다.

  • 필요할 때 헬퍼 클래스를 사용하여 엔티티를 초기화하십시오.

Less work, useful when OEMIV is not at option, for example in a Swing application, but may be useful too on repository implementations to initialize any entity in one shot.

For the last option, I wrote a utility class, JpaUtils to initilize entities at some deph.

For example:

@Transactional
public class RepositoryHelper {

    @PersistenceContext
    private EntityManager em;

    public void intialize(Object entity, int depth) {
        JpaUtils.initialize(em, entity, depth);
    }
}

it can only be lazily loaded whilst within a transaction. So you could access the collection in your repository, which has a transaction - or what I normally do is a get with association, or set fetchmode to eager.


I think you need OpenSessionInViewFilter to keep your session open during view rendering (but it is not too good practice).


You can do the same like this:

@Override
public FaqQuestions getFaqQuestionById(Long questionId) {
    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    FaqQuestions faqQuestions = null;
    try {
        faqQuestions = (FaqQuestions) session.get(FaqQuestions.class,
                questionId);
        Hibernate.initialize(faqQuestions.getFaqAnswers());

        tx.commit();
        faqQuestions.getFaqAnswers().size();
    } finally {
        session.close();
    }
    return faqQuestions;
}

Just use faqQuestions.getFaqAnswers().size()nin your controller and you will get the size if lazily intialised list, without fetching the list itself.

참고URL : https://stackoverflow.com/questions/15359306/how-to-load-lazy-fetched-items-from-hibernate-jpa-in-my-controller

반응형