컨트롤러의 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.
'Programing' 카테고리의 다른 글
$ 적용 진행 중 오류 (0) | 2020.07.02 |
---|---|
변수를 설정하고 사용하는 CMake 구문은 무엇입니까? (0) | 2020.07.02 |
표준 정규화와 달리 softmax를 사용하는 이유는 무엇입니까? (0) | 2020.07.02 |
BigDecimal 세트 스케일 및 라운드 (0) | 2020.07.02 |
C를 사용하여 배열 반환 (0) | 2020.07.02 |