웹 애플리케이션과 영속성 관리
- 스프링 환경에서 JPA를 사용하면 컨테이너가 트랜잭션과 영속성 컨텍스트를 관리해주므로 애플리케이션을 쉽게 개발할 수 있음
- 하지만 컨테이너 환경에서 동작하는 JPA의 내부 동작 방식을 이해하지 못하면 문제발생시 해결하기가 쉽지 않음
트랜잭션 범위의 영속성 컨텍스트
- 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용
- 이름 그대로 트랜잭션의 범위와 영속성 컨텍스트의 생존 범위가 같음
- 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날때 영속성 컨텍스트를 종료
- 같은 트랙잭션 안에서는 같은 영속성 컨텍스트에 접근
[트랜잭션 범위의 영속성 컨텍스트 그림]
@Transactional
어노테이션을 사용- 해당 어노테이션으로 인해 메소드 시작선 트랙잭션 AOP가 먼저 동작
- 메소드가 정상 종료되면 트랜잭션 커밋
- 영속성 컨텍스트를 플러시해서 변경사항을 데이터베이스에 반영
-
예외가 발생하면 트랜잭션을 롤백, 플러스를 호춣하지 않음
- 트랜잭션이 종료되면 영속성 컨텍스트를 종료
- 이때 트랜잭션 범위안에 있던 엔티티들은 준영속 상태
- 컨트롤러에 엔티티가 반환되도 준영속 상태의 엔티티
- 트랜잭션이 같으면 같은 영속성 컨텍스트를 사용
- 한 트랜잭션 내에서 다양한 엔티티 메니저를 주입받아 사용해도 같은 영속성 컨텍스트에 존재
- 트랜잭션이 다르면 다른 영속성 컨텍스트를 사용
- 즉 스레드마다 각각 다른 트랜잭션을 할당, 영속성 컨텍스트가 다르므로 멀티스레드에 안전
준영속 상태와 지연 로딩
- 준영속 상태와 변경 감지
- 준영속 상태와 지연 로딩
- 뷰가 필요한 엔티티를 미리 로딩
- OSIV를 사용해 엔티티를 항상 영속 상태로 유지
- 글로벌 패치 전략 수정
- 지연로딩이 아닌 즉시로딩 사용
- N+1 발생
- JPQL 페치 조인
- N+1은 해결되지만 화면에 맞는 조회에 대한 메소드가 리포지토리에 무분별하게 증가할 수 있음, 뷰와 리포지토리간의 의존성 발생
- 무조건 함께 조회(페치 조인)하도록 할 수도 있음
select o
from Order o
join fetch o.member
- 강제로 초기화
class OrderService {
@Transactional
public Order findOrder(id) {
Order order = orderRepository.findOrder(id); // Member는 지연로딩
order.getMember().getName(); // 프록시 객체를 강제로 초기화
return order;
}
}
- FACADE 계층 추가
- 프리젠테이션 계층과 서비스 계층 사이에 FACADE 계층을 더 두어 프록시 초기화가 필요한경우 이 계층에서 수행
- FACADE 계층의 역할과 특징
- 프리젠테이션 계층과 도메인 모델 계층 간의 논리적 의존성 분리
- 프리젠테이션 계층에서 필요한 프록시 객체 초기화
- 위 예제에서는 해당 코드를 FACADE 계층에서 수행
order.getMember().getName();
- 결국 더 많은 코드작성이 필요함
OSIV
- OSIV(Open Session In View)는 영속성 컨텍스트를 뷰까지 열어두는 것
-
뷰에서도 지연로딩 사용가능
-
과거 OSIV: 요청 당 트랜잭션 [그림]
- 뷰를 랜더링한후 트랜잭션 커밋
- 즉 영속성 텍스트트를 플러시
- 프리젠테이션 계층에서 데이터 변경이 가능해지므로 유지보수를 하기 힘듬
- 이를 해결하기 위한 다음과 같은 방법이 존재
- 엔티티를 읽기 전용 인터페이스로 제공
- 엔티티 래핑
- DTO만 반환
- 스프링 OSIV: 비즈니스 계층 트랜잭션
- OSIV를 사용하기는 하지만 트랜잭션은 비즈니스 계층에서만 사용
- 스프링 필터, 인터셉터에서 영속성 컨텍스트를 생성
- 서비스 계층 진입시 트랜잭션 시작
- 서비스 계층 종료시 트랜잭션 커밋, 영속성 컨텍스트 플러시
- 스프링 필터, 인터셉터에 요청이 돌아오면 영속성 컨텍스트 종료, 이때 플러시 호출 안함
-
트랜잭션 범위 밖에서 영속성 컨텍스트가 엔티티를 조회, 이것을 트랜잭션 없이 읽기(Nontransactional reads)라고 함
- 스프링 OSIV 주의사항
- 프리젠테이션 계층에서 엔티티를 수정후 트랜잭션을 시작하는 서비스계층을 호출하는 경우
- 프리젠테이션 계층에서 변경한 엔티티가 서비스계층이 끝날때 플러시됨