JPA에서 항상 생각을 해야되는 것이 바로 영속상태
, 영속성 컨텍스트위에 있는가?
를 생각해야된다.
예를 들어 우리는 DAO층에서 실제 DB와 통신을 보편적으로 진행하는데, 이쪽은 영속성 컨텍스트에 의해 관리가 되어
영속 상태를 유지한다.
하지만, Controller, View 이런 계층에서는 준영속 상태가 된다. 그래서 영속상태와 다르게 변경감지, 지연로딩이 동작하지 않게 된다.
지연로딩이 동작하지 않기 때문에 이때 지연 로딩을 시도하면 문제가 발생하는게 당연하다.
하이버네이트가 구현체라면 org.hibernate.LazyInitializationException
이 발생한다.
이것을 해결하는 문제는 두가지가 있다.
이름 그대로 영속성 컨텍스트가 살았을 때, 필요한 엔티티를 다 로딩하거나 초기화해서 반환하는 방법이다.
이미 다 로딩했기 떄문에, 지연 로딩이 발생할 걱정을 안해도 된다.
이 방법에서는 로딩해두는 방법에 따라 3가지로 나뉜다.
엔티티에서 fetch 타입을 변경하면 애플리케이션 전체에서 이 엔티티 객체를 로드할때 마다 해당 전략을 사용하므로 글로벌 페치 전략이라고 한다.
단점
사용하지 않는 엔티티까지 로딩
N + 1 문제 발생
JPQL을 사용할 때 문제가 발생하는데
Order와 Member가 다대일로 연결되어 있다고 가정한 후에
select o from Order o
를 사용해서 조회를 한다고 한다면
글로벌 페치 전략을 사용하지 않고 그냥 JPQL자체만 사용하기 때문에
1. 일단 Order를 조회
2. order 인스턴스들 생성
3. member 페치 전략이 즉시로딩이므로 order조회되면 member도 조회
4. 근데 영속성 컨텍스트에 없다? order엔티티 수만큼 계속 조회
이걸 해결하려고 나온것이 바로 아래 부분이다.
여기선 join fetch
를 사용하면 된다고 한다.
select o from Order o join fetch o.member
N + 1 자체의 문제를 해결해주는것은 좋다. 근데 이제 order만 조회하느냐, order에 연관된 member까지 조회하냐에 따라 메소드를 늘려야할 것이다. 이러면 내부적으로 논리적 의존관계 우려가 발생하기 때문에 잘 고려해서 사용해야 할 것이다.
지연 로딩을 설정했을 때 연관된 엔티티는 프록시 객체이다.
이 가짜 객체는 실제 사용 시점에 초기화가 되는데 이것을 영속성이 살아있을 때 다 초기화를 하여 반환해준다면? 준영속에서도 사용이 가능하다.
뷰를 위한 프록시 초기화 담당 계층
서비스와 컨트롤러를 분리해서 그 계층 사이의 의존성을 한번 더 분리해주는 것이라고 생각하면 편하다.
Controller - Facade - Service 이렇게 말이다.
프록시를 초기화하려면 영속성 컨텍스트가 필요해서 Facade에서 트랜잭션을 시작해야 한다.
퍼사드 계층 역할 및 특징
이것도 근데 결국 프리젠테이션 계층에서의 준영속 상태 라는 것이 문제이기 때문에 고안해낸 것.
OSIV(Open Session In View)는 영속성 컨텍스트를 뷰까지 열어준다는 뜻.
뷰에서도 지연로딩이 가능❗️
프리젠테이션 계층에서 엔티티를 수정못하게 막는 방법은
들이 있다.
True - 기본값
application.properties에 추가해주면 된다.
sping.jpa.open-in-view:true
장점
단점
False - 설정값
sping.jpa.open-in-view : false
DB 커넥션을 Transaction
내부까지만 유지
(트랜잭션은 Service에서 수행되니까 Service까지만 유지되는 것)
Service / Repository
에서 해결해야 함결국 OSIV는 DB 커넥션 리소스에 대한 효율적인 사용과 관련된 전략이다.
OSIV 실무 TIP
실시간 API 고객 서비스를 해야한다면 OSIV false
설정
ADMIN 처럼 커넥션이 많지 않은 곳 OSIV true
설정