(JPA - 6) 트랜잭션

영속성 컨텍스트의 내용이 트랜잭션이 커밋 될때 실제 데이터베이스에 반영이 된다.
따라서 JPA에서 영속성 컨텍스트와 트랜잭션이 어떻게 관리되는지 아는 것이 매우 중요하다.

트랜잭션 범위

J2SE 환경에서 트랜잭션

엔티티 매니져로 부터 EntityTransaction 을 얻어서 트랜잭션을 관리한다.
J2SE 환경에서는 엔티티 매니져마다 다른 영속성 컨텍스트를 사용한다.

1
2
3
4
5
6
7
8
EntityTransaction tx = entityManger.getTransaction();

tx.begin(); // -- 트랜잭션 시작
Customer newCustomer = new Customer(); // NEW 상태
entityManger.persist(newCustomer); // 영속(Managed) 상태
tx.commit(); // -- 트랜잭션 종료

newCustomer.getName(); // 준영속(Detached) 상태

Spring과 같은 컨테이너 환경에서 트랜잭션

@Transactional 어노테이션을 선언해서 트랜잭션을 관리한다.
스프링 컨테이너는 기본적으로 트랜잭션 범위 내에서만 영속성 컨텍스트가 유효 하도록 설정되어 있다.
컨테이너 환경에서는 엔티티 매니져가 같은 영속성 컨텍스트를 공유 할 수 도 있다.

  • 같은 트랜잭션 내에서는 같은 영속성 컨텍스트를 사용한다.
  • 여러 요청을 받아 각 쓰레드에서 동일한 엔티티매니져를 사용하더라도,
    다른 트랙잭션 내에서는 다른 영속성 컨텍스트를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Controller
public class CustomerController {
@Autowired CustomerService customerService;
public void doSome(...) {
// customer 엔티티는 트랜잭션 범위 밖이기 때문에 준영속(detached) 상태이다.
Customer customer = customerService.doSomeThing1();
}
}
@Service
public class CustomerService {
....
@Transactional
public Customer doSomeThing1() {
// 트랜잭션 시작
// do something
// 트랜잭션 종료
}
....
}

트랜잭션 밖의 준영속 상태에서 지연로딩

앞에서 본 것과 같이 트랜잭션 밖에 있는 엔티티는 준영속 상태이다.
준영속 상태 에서 지연로딩 이 발생하는 경우 예외가 발생 한다.

1
2
Customer some = customerService.doSomeThing1();
some.getOrder(); // 지연 로딩. 예외 발생

이를 해결하기 위한 다양한 방법이 존재한다.

  • 글로벌 페치 전략을 즉시로딩(FetchType.EAGER)으로 수정
    • 사용하지 않는 엔티티가 로딩 됨
    • N+1 문제가 발생
  • 해당 경우만 JPQL fetch 조인을 사용
    • Repository에 메소드가 늘어난다.
  • 트랜잭션 내에서 프록시 객체를 강제로 초기화
    • 프레젠테이션 계층을 위해 서비스 계층의 로직을 수정해야 됨
  • FACADE 계층 추가하여 프레젠테이션과 서비스 계층의 의존성을 분리
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Service
    public class CustomerFacade {
    @Autowired CustomerService customerService;

    @Transactional // 트랜잭션 시작은 Facade 계층에서 시작
    public Customer doSomeThing1() {
    Customer customer customerService.doSomeThing1();
    customer.getOrder(); // 프록시 객체를 강제로 초기화
    return customer;
    }
    }
    @Service
    public class CustomerService {

    public Customer doSomeThing1() {

    }
    }

OSIV

OSIV(Open Session In View) 는 트랜잭션 범위 밖인 프레젠테이션 영역에서
지연로딩이 안되는 문제를 해결하기 위해 영속성 컨텍스트의 범위를 View 계층까지 열어 두는 것이다.
하지만, 엔티티가 변경되는 경우 View 단까지 수정이 생긴다.
따라서 OSIV를 사용하는 것보다는 DTO를 사용하는 것이 안전하다.

JPA의 표준 용어는 OEIV(Open EntityManager In View)이지만 Hibernate에 의해서 관습적으로 OSIV로 불린다.

설정

아래와 같이 설정하면 엔티티매니져에 OpenEntityManagerInViewInterceptor 가 등록된다.

1
2
3
spring:
jpa:
open-in-view: true

Spring에서 OSIV

  1. 클라이언트의 요청이 들어오면 영속성 컨텍스트를 생성하고 요청이 끝날 때까지 유지한다.
  2. 엔티티에 대한 수정은 트랜잭션 범위 내에서만 가능하다.
  3. 트랜잭션 범위 밖에서는 조회만 가능하다.(지연로딩도 가능)
  4. 동일 요청내의 여러 트랜잭션에서 같은 영속성 컨텍스트를 사용한다.
    즉, 한 트랜잭션이 종료되고, 엔티티의 속성을 수정한 경우 다른 트랜잭션이 시작할 때
    해당 수정이 반영된다. 의도하지 않은 일이 발생할 수도 있다.
Share