JPA 지연로딩 문제
jpa를 이용해서 엔티티의 연관 객체를 지연로딩(fetch = FetchType.LAZY)로 선택한 경우, 트랜잭션 범위 밖에서 로딩되지 않은 객체를 조회할 경우 exception 이 발생한다.
위 방법을 해결하기 위한 방법을 알아보자.
즉시로딩 설정
FetchType.EAGER 로 설정하면 즉시 로딩 전략으로 트랜젝션 밖인 프레젠테이션 계층 등에서 조회시 문제가 발생하지 않는다.
- 즉시로딩이지만, 사용하지 않는 프레젠테이션 계층에선 필요없는 데이터까지 조회하게 된다.
- em.find()를 이용하는 경우 문제가 발생하지 않지만, jpql을 사용하는 경우 N+1 문제가 발생한다.
- jpql을 이용해 select m from Member m 을 실행하면 Member만 가져온후, Member수만큼 연관된 데이터를 select문을 수행한다.
- fetch join 을 통해 문제 해결이 가능하다.
jpql fetch join 사용
연관 객체가 프레젠테이션 계층에서 필요한 경우 jpql fetch join 을 사용하도록 한다.
연관 객체가 필요없는 프레젠테이션도 있겠지만, 그렇다고 메소드를 두개 나누면 논리적 의존이 발생하기 때문에 잘 타협하는 것이 중요하다.
강제로 초기화
order.getMember().getName() 과 같이 강제로 초기화를 서비스 계층에서 실행하는 것이다. 이또한 논리적으로 프레젠테이션 계층과 의존이 발생하므로 좋지 않다.
Facade 계층 추가
- 프레젠테이션 계층과 도메인 계층 간 논리적 의존성을 분리해준다.
- 프레젠테이션 계층에서 필요한 프록시 객체를 초기화 한다.
- 리포지토리를 직접 호출해서 뷰가 요구하는 엔티티를 찾는다.
단점
Facade 계층이 추가됨에 따라 하나의 계층 추가로 인한 작업 효율 감소와 단순히 서비스 계층 호출을 위임만 하는 코드만 남을 가능성도 있다.
OSIV (Open Session In View)
트랜잭션을 뷰에서까지 살아있도록 유지하는 방법이다.
단점
- 프레젠테이션 계층에서 엔티티 수정이 가능하게 되어 버린다.
현재는 거의 사용되지 않고, 타협하여 비즈니스 계층 에서만 트랜잭션을 유지하는 방식의 OSIV를 사용한다.
(스프링도 비즈니스 계층까지만 유지되는 OSIV 사용)
스프링 osiv : 서비스 계층이 종료되면 트랜잭션만 종료하고, persistence context 는 살려둔다. 연관 객체 조회시 트랜잭션 없이 select 를 수행하여 조회할 수 있다.
하이버네이트는 영속 엔티티의 컬렉션을 별도 하이버네이트 구현체로(래퍼)로 감싸기 때문에,
엔티티 필드에 컬렉션 선언시 즉시 초기화 하는 것을 권장한다.
ex) Collection<Member> members = new ArrayList<Member>();
'JAVA' 카테고리의 다른 글
JPA 엔티티 생명주기에 리스너 등록 (0) | 2021.04.28 |
---|---|
JPA Entity와 DB값 간 매핑 (0) | 2021.04.28 |
JPA @ManyToMany (0) | 2021.04.23 |
JPA @Inheritance (0) | 2021.04.23 |
JPA CASCADE 종류 (0) | 2021.04.22 |