트랜잭션 범위와 영속성 컨텍스트
- 스프링이나 J2EE 컨테이너 환경에서 JPA를 사용하면 컨테이너가 제공하는 전략을 따라야 한다.
- 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
- 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 종료한다.
- 같은 트랜잭션 안에서는 항상 같은 영속성 컨텍스트에 접근한다.
스프링 트랜잭션 AOP
- @Transactional 어노테이션을 선언하면 호출한 메서드를 실행하기 직전에 스프링 트랜잭션 AOP가 동작한다.
- 스프링 트랜잭션 AOP는 대상 메서드를 호출하기 직전에 트랜잭션을 시작하고, 대상 메서드가 정상 종료되면 트랜잭션을 커밋하면서 종료된다.
- 이때 트랜잭션을 커밋하면 JPA는 먼저 영속성 컨텍스트를 플러시해서 변경 내용을 데이터베이스에 반영한 후, 데이터베이스 트랜잭션을 커밋한다.
- 만약 예외가 발생하면 트랜잭션을 롤백하고 종료하는데 이때는 플러시를 호출하지 않는다.
- 트랜잭션 볌위의 영속성 컨텍스트 전략은 다양한 위치에서 엔티티 매니저를 주입받아 사용해도, 트랜잭션이 같으면 항상 같은 영속성 컨텍스트를 사용한다.
- 스프링 컨테이너는 스레드마다 각각 다른 트랜잭션을 할당하기 때문에, 같은 엔티티 매니저를 호출해도 접근하는 영속성 컨텍스트가 다르다. → 멀티 스레드 상황에 안전
준영속 상태와 지연 로딩
- 조회한 엔티티는 서비스와 리포지토리 계층에서는 영속성 컨텍스트에 의해 관리되지만, 컨트롤러나 뷰 같은 프레젠테이션 계층에서는 준영속 상태가 된다.
- 따라서 트랜잭션이 없는 프레젠테이션 계층에서는 변경 감지와 지연 로딩이 동작하지 않는다.
- 준영속 상태의 지연 로딩 문제를 해결하기 위한 방법에는 뷰가 필요한 엔티티를 미리 로딩해두는 방법과, OSIV를 사용해서 엔티티를 항상 영속 상태로 유지하는 방법이 있다.
엔티티 미리 로딩
JPQL 페치 조인
- JPQL을 호출하는 시점에 함께 로딩할 엔티티를 선택하는 방법으로, SQL JOIN을 사용해서 페치 조인 대상까지 함께 조회한다.
- 하지만 페치 조인을 무분별하게 사용하면 화면에 맞춘 리포지토리 메소드가 증가할 수 있고, 결국 프레젠테이션 계층이 데이터 접근 계층을 침범하게 된다.
- 따라서 무분별한 최적화로 프레젠테이션 계층과 데이터 접근 계층 간에 의존관계를 증가시키는 것보다, 적절한 선에서 타협점을 찾는 것이 좋다.
강제 초기화
- 영속성 컨텍스트가 살아있을 때 프레젠테이션 계층이 필요한 엔티티를 강제로 초기화해서 변환하는 방법
- 프록시를 초기화하는 역할을 서비스 계층이 담당하면 뷰에 필요한 엔티티에 따라 서비스 계층의 로직을 변경해야 하는 문제가 생긴다.
- 따라서 프레젠테이션 계층과 서비스 계층 사이에 FACADE 계층을 하나 더 두고, 뷰를 위한 프록시 초기화를 FACADE 계층에서 담당한다. (FACADE 계층에서 트랜잭션을 시작해야 한다.)
- 즉, FACADE 계층은 프레젠테이션 계층과 도메인 모델 계층 간의 논리적 의존성을 분리해주고, 프레젠테이션 계층에서 필요한 프록시 객체를 초기화한다.
- 또한 서비스 계층을 호출해서 비즈니스 로직을 실행하고, 리포지토리를 직접 호출해서 뷰가 요구하는 엔티티를 찾는다.
스프링 OSIV
- OSIV는 Open Session In View의 약자로, 영속성 컨텍스트를 뷰까지 열어둔다는 뜻이다.
- 스프링 OSIV는 비즈니스 계층 트랜잭션 OSIV로, 영속성 컨텍스트는 뷰까지 열어 두지만 트랜잭션은 비즈니스 계층에서만 사용한다.
동작 과정
- 클라이언트의 요청이 들어오면 서블릿 필터나 스프링 인터셉터에서 영속성 컨텍스트를 생성하고, 트랜잭션은 시작하지 않는다.
- 서비스 계층에서 트랜잭션을 시작할 때 미리 생성해둔 영속성 컨텍스트를 찾아와서 트랜잭션을 시작한다.
- 서비스 계층이 끝나면 트랜잭션을 커밋하고 영속성 컨텍스트를 플러시한다.
- 컨트롤러와 뷰까지 영속성 컨텍스트가 유지되므로, 조회한 엔티티는 영속 상태를 유지한다.
- 서블릿 필터나 스프링 인터셉터로 요청이 돌아오면 영속성 컨텍스트를 종료하는데, 이때 플러시를 호출하지 않고 바로 종료한다.
트랜잭션 없이 읽기
- 엔티티를 변경하지 않고 단순히 조회만 할 때는 트랜잭션이 없어도 되는데, 이것을 트랜잭션 없이 읽기라고 한다.
- 스프링이 제공하는 비즈니스 계층 트랜잭션 OSIV를 사용하면 프레젠테이션 계층은 엔티티를 수정할 수 없지만, 트랜잭션 없이 읽기를 사용해서 지연 로딩이 가능하다.
단점
- 스프링 OSIV를 적용하면 같은 영속성 컨텍스트를 여러 트랜잭션이 공유하므로 주의해야 한다.
- 프레젠테이션 계층에서 엔티티를 수정하고 나서 비즈니스 로직을 수행하면 엔티티가 수정될 수 있다.
- 프레젠테이션 계층에서 지연 로딩에 의한 SQL이 실행되므로, 성능 튜닝 시 확인해야 할 범위가 넓다.
'Spring > JPA' 카테고리의 다른 글
프록시와 연관관계 관리 (0) | 2022.03.25 |
---|---|
엔티티 매핑 (0) | 2022.03.25 |
영속성 컨텍스트와 연속성 관리 (0) | 2022.03.05 |
N+1 문제와 해결 방법 (0) | 2022.03.05 |
JPA와 ORM (0) | 2022.02.23 |