트랜잭션 범위 영속성 컨텍스트

  • spring container는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
  • 트랜잭션 범위가 영속성 컨텍스트 생존 범위와 같다는 뜻이다.
  • 비즈니스 로직을 시작하는 서비스 계층에 @Transactional로 트랜잭션을 시작한다.
  • 메소드를 실행하면서 트랜잭션을 시작하고 끝내면서 트랜잭션을 끝낸다. AOP로 말이다.
  • 트랜잭션을 커밋하면서 JPA는 영속성 컨텍스트를 플러시해서 변경 내용을 DB에 반영한 후에 DB 트랜잭션을 커밋한다.
  • 예외가 발생하면 트랜잭션을 롤백하고 종료하는데, 이때는 플러시를 호출하지 않는다.

트랜잭션이 같으면 같은 영속성 컨텍스트를 사용

  • 트랜잭션 범위의 영속성 컨텍스트 전략은 다양한 위치에서 엔티티 매니저를 주입받아서 사용해도 트랜잭션이 같으면 항상 같은 영속성 컨텍스트를 사용한다.

트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다.

  • 같은 엔티티 매니저를 사용해도 트랜잭션에 따라 접근하는 영속성 컨텍스트가 다르다.

준영속 상태와 지연 로딩

  • 트랜잭션이 끝나면 준영속 상태로 돌입한다.
  • 변경 감지는 영속성 컨테스트가 살아 있는 서비스 계층까지만 동작하고 영속성 컨텍스트가 종료되면 동작하지 않는다.
  • 만약 프레젠테이션 계층에서도 동작한다면 각 계층이 가지는 책임들이 모호해진다.
  • 추가로 지연로딩도 동작하지 않는다. 프록시 객체로 조회했다고 했을 때, 초기화를 시도하지만 영속성 컨텍스트가 없으므로 지연 로딩이 불가능해진다.
  • 지연 로딩을 시도하면 org.hibernate.LazyInitializationExcption이 난다.
    1. 뷰에 필요한 엔티티를 미리 로딩

      글로벌 페치

      • @ManyToOne(fetch = FetchType.EAGER)오 같이 로딩 전략을 세운다.
        • 단점으로
          1. 사용하지 않는 엔티티를 로딩
          2. N + 1 발생

      JPQL 페치 조인

      • join fetch를 이용하면 join을 이용해서 페치 조인 대상까지 함께 조회한다. (N + 1 방지)
        • 단점으로
          1. 화면에 맞춘 리포지토리가 증가할 수 있다.( 프리젠테이션이 알게 모르게 간섭하는 것이다.)

      강제로 초기화

      • 영속성 컨텍스트가 살아있을 때 프리젠테이션에 필요한 엔티티를 강제로 초기화하면 된다.
        • get~로 부르거나
        • org.hibernate.Hibernate.initialize()로 강제 초기화하면 된다.
        • JPA 표준에는 초기화 메소드가 없다.
          PersistenceUnitUtil persistenceUnitUtil = entityManager.getEntityManagerFactory().getPersistenceUtniUtil();
          boolean isLoaded = persistenceUnitUtil.isLoaded(~);   
          
      • 위와 같이 초기화 여부를 확인할 수 있다.
    1. OSIV(OpenSessionInView)를 사용해서 항상 영속 상태로 유지

4 버전 기준

  • 하이버네이트 OSIV 서블릿 필터 : org.springframework.orm.hibernate4.support.OpenSessionInViewFilter
  • 하이버네이트 OSIV 스프링 인터셉터 : org.springframework.ozm.hibernate4.support.OpenSessionInViewInterceptor
  • JPA OEIV 서블릿 필터 : org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
  • JPA OEIV 스프링 인터셉터 : org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor
  • 클라이언트 요청이 들어오면 영속성 컨텍스트를 생성한다. (트랜잭션은 시작하지 않는다.)
  • 트랜잭션을 시작하면 생성해둔 영속성 컨텍스트에 트랜잭션을 붙이고 트랜잭션을 커밋하면 영속성 컨텍스트를 플러시한다.
  • 기본적으로 변경은 트랜잭션 안에서 해야 한다. 그게 아니라면 jakarta.persistence.TransactionRequiredException이 발생한다.
  • 단순 조회만이라면 Nontransactional reads가 가능하다. 프록시 초기화하는 지연 로딩도 조회이므로 트랜잭션 범위 밖에서 가능하다.