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 설명필요한 경우
감사.
답변
초기화를 위해 게으른 컬렉션을 명시 적으로 호출해야합니다 (일반적인 방법은 .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를 사용하여 전체 요청에 대해 세션을 열린 상태로 유지하십시오.
웹 환경에서 일반적으로 허용되는 작업이 줄어 듭니다.
- 필요할 때 헬퍼 클래스를 사용하여 엔티티를 초기화하십시오.
OEMIV가 옵션이 아닌 경우 (예 : Swing 응용 프로그램) 유용하지만 저장소 구현에서 엔티티를 한 번에 초기화하는 데 유용 할 수도 있습니다.
마지막 옵션으로 유틸리티 클래스 JpaUtils를 작성했습니다. 일부 deph에서 엔티티를 초기화했습니다.
예를 들면 다음과 같습니다.
@Transactional
public class RepositoryHelper {
@PersistenceContext
private EntityManager em;
public void intialize(Object entity, int depth) {
JpaUtils.initialize(em, entity, depth);
}
}
답변
트랜잭션 내에서만 지연로드 할 수 있습니다. 따라서 저장소가있는 컬렉션에 액세스 할 수 있습니다.이 저장소에는 트랜잭션 이 get with association
있습니다.
답변
뷰 렌더링 중에 세션을 열어 두려면 OpenSessionInViewFilter 가 필요하다고 생각합니다 (그러나 좋은 습관은 아닙니다).
답변
스프링 데이터 JpaRepository
스프링 데이터 JpaRepository
는 다음 두 가지 방법을 정의합니다.
getOne
하위 엔터티를 유지할 때 또는 부모 연결 을 설정하는 데 적합한 엔터티 프록시 를 반환합니다 .@ManyToOne
@OneToOne
findById
연관된 테이블에서 엔티티를로드하는 SELECT 문을 실행 한 후 엔티티 POJO를 리턴합니다.
그러나 귀하의 경우에, 당신은 전화 중 하나를하지 않았 getOne
거나 findById
:
Person person = personRepository.findOne(1L);
따라서 findOne
메소드가에서 정의한 메소드 라고 가정 합니다 PersonRepository
. 그러나이 findOne
방법은 귀하의 경우에별로 유용하지 않습니다. Person
with with roles
collection 을 가져와야하므로 findOneWithRoles
대신 메소드 를 사용하는 것이 좋습니다.
커스텀 스프링 데이터 메소드
PersonRepositoryCustom
다음과 같이 인터페이스 를 정의 할 수 있습니다 .
public interface PersonRepository
extends JpaRepository<Person, Long>, PersonRepositoryCustom {
}
public interface PersonRepositoryCustom {
Person findOneWithRoles(Long id);
}
그리고 다음과 같이 구현을 정의하십시오.
public class PersonRepositoryImpl implements PersonRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public Person findOneWithRoles(Long id)() {
return entityManager.createQuery("""
select p
from Person p
left join fetch p.roles
where p.id = :id
""", Person.class)
.setParameter("id", id)
.getSingleResult();
}
}
그게 다야!
답변
다음과 같이 똑같이 할 수 있습니다 :
@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;
}
컨트롤러에서 faqQuestions.getFaqAnswers (). size ()를 사용하면 목록 자체를 가져 오지 않고 느리게 목록 화 된 경우 크기를 얻을 수 있습니다.