책 [자바 ORM 표준 JPA 프로그래밍]을 참고하여 작성하였습니다.
1. 트랜잭션 범위의 영속성 컨텍스트
1) 스프링 컨테이너의 기본 전략
- 스프링 컨테이너는 트랜잭션 범위의 영속성 컨테이너 전략을 기본으로 사용한다.
(=트랜잭션 범위와 영속성 컨텍스트의 생존 범위가 같다)
- 비즈니스 로직을 시작하는 서비스 계층에
@Transactional 애노테이션을 선언해서 트랜잭션을 시작
- 이 애노테이션이 있으면 호출한 메소드를 실행하기 직전에 스프링의 트랜잭션 AOP가 먼저 동작
- 트랜잭션 AOP는 대상 메서드 호출 전 트랜잭션 시작 & 대상 메서드 정상 종료시 트랜잭션 커밋
- 트랜잭션을 커밋하면 JPA는 먼저 영속성 컨텍스트를 플러시해서 변경 내용을 DB에 반영 -> 그다음에 DB 트랜잭션 커밋
- 예외가 발생하면 트랜잭션을 롤백(커밋x)하고 종료하는데 이때는 플러시 호출 x
- 다양한 위치에서 엔티티 매니저를 주입 받아 사용해도 트랜잭션이 같으면 항상 같은 영속성 컨텍스트 사용
- 트랜잭션이 다르면, 같은 엔티티 매니저를 사용해도 트랜잭션에 따라 접근하는 영속성 컨텍스트가 다르다.
2. 준영속 상태와 지연 로딩
준영속 상태와 변경 감지
- 변경 감지 기능은 서비스 계층까지만 동작하고, 프리젠테이션 계층에서는 동작하지 않음
- 비즈니스 로직은 서비스 계층에서 끝내고, 프리젠테이션 계층은 데이터를 보여주는 데 집중해야 한다. 따라서 이는 문제가 되지 않음
준영속 상태와 지연 로딩
- 준영속 상태는 영속성 컨텍스트가 없으므로 지연로딩을 할 수 없다.
해결 방법 1: 뷰가 필요한 엔티티를 미리 로딩해두기
- 방법1. 글로벌 페치 전략 수정
@ManyToOne(fetch = FetchType.EAGER)
- 방법2. JPQL 페치 조인
- JPQL:
select o from Order o -> select o from Order o join fetch o.member로 변경
- 방법3. 강제로 초기화
- 영속성 컨텍스트가 살아있을 때 프리젠테이션 계층이 필요한 엔티티를 강제로 초기화해서 반환
- 예시 :
order.getMember().getName()
- 비즈니스 로직을 담당하는 서비스 계층에서 이처럼 프리젠테이션 계층을 위한 일(프록시 초기화)까지는 하는 것은 좋지 않다.
- 이때는 프리젠테이션 계층과 서비스 계층 사이에 FACADE 계층을 하나 더 두어 서비스 계층과 프리젠테이션 계층 사이에 논리적인 의존성을 분리할 수 있다. (이때는 FACADE 계층에서 트랜잭션을 시작한다.)
해결방법 2: OSIV를 사용해서 엔티티를 항상 영속 상태로 유지하기
- OSIV = Open Session In View. 영속성 컨텍스트를 뷰까지 열어둔다는 것.
- 그러면 뷰에서도 지연로딩이 가능함.
1) 요청 당 트랜잭션 방식의 OSIV
2) 스프링 OSIV : 비즈니스 계층 트랜잭션
- OSIV를 사용하기는 하지는 트랜잭션은 비즈니스 계층에서만 사용한다.
- 영속성 컨텍스트를 프리젠테이션 계층까지 유지한다.
- 프리젠테이션 계층에는 트랜잭션이 없으므로 엔티티를 수정할 수 없다. (프리젠테이션 계층에서 수정해도 수정 내용을 db에 반영하지 않는다.)
- 프리젠테이션 계층에는 트랜잭션이 없지만
트랜잭션 없이 읽기를 사용해서 지연로딩을 할 수 있다.
- 종류
- 하이버네이트 OSIV 서블릿 필터
- 하이버네이트 OSIV 스프링 인터셉터
- JPA OSIV 서블릿 필터
- JPA OEIV 스프링 인터셉터
- 동작 원리
- 클라이언트 요청이 들어오면
서블릿 필터나 스프링 인터셉터에서 영속성 컨텍스트 생성 (아직 트랜잭션 시작x)
- 서비스 계층에서 트랜잭션을 시작하면(@Transactional) 앞에서 생성해둔 영속성 컨텍스트에 트랜잭션 시작
- 비즈니스 로직을 실행하고 서비스 계층이 끝나면 트랜잭션을 커밋하면서 영속성 컨텍스트를 플러시(트랜잭션만 종료하고 영속성 컨텍스트는 살려둠)
서블릿 필터나 스프링 인터셉터로 요청이 들어오면(클라이언트 요청이 끝나면) 영속성 컨텍스트를 종료 (플러시 호출하지 않고 바로 종료)
- 단점
- OSIV를 적용하면 같은 영속성 컨텍스트를 여러 트랜잭션이 공유 가능
- 프리젠테이션 계층에서 엔티티를 수정하고나서 비즈니스 로직을 수행하면 엔티티가 수정될 수 있다.
- 프리젠테이션 계층에서 지연로딩에 의한 SQL이 실행되기 때문에 성능 튜닝 시에 확인할 부분이 넓다.