KKanging

[JPA] Context 주기와 트랜잭션 주기 그리고 OSIV 본문

백엔드/JPA

[JPA] Context 주기와 트랜잭션 주기 그리고 OSIV

천방지축 개발자 2024. 5. 8. 06:51

스프링의 @Transactional 어노테이션과 트랜잭션 범위

  • 스프링의 Service 계층에 @Transactional 어노테이션을 많이 봤을 것이다.
  • 스프링의 @Transactional 어노테이션은 AOP로 구현되었으며, 메소드 시작할때 영속성 컨텍스트와 트랜잭션을 시작한다.
  • 대상 메소드가 종료하면 트랜잭션을 커밋하고 종료한다.
  • 트랜잭션을 종료하면 영속성 컨텍스트도 종료하고 같은 트랜잭션에는 같은 영속성 컨텍스트를 사용한다.
  • 트랜잭션을 커밋하면 컨텍스트를 플러시하고 변경 내용을 데배에 반영한다.
    • 그리고 데이터베이스의 트랜잭션을 커밋한다.
    • 따라서 영속성 컨텍스트의 변경 내용이 데이터베이스에 정상 반영된다.
    • 만약 트랜잭션의 예외가 발생하면 트랜잭션을 종료하는데 이때는 플러시를 하지 않는다.
요약

스프링의 트랜잭션 어노테이션은 -> AOP로 구현됨
트랜잭션의 주기와 영속성 컨텍스트의 생존 주기가 같음
같은 트랜잭션이면 같은 영속성 컨텍스트의 접근함

만약 준영속 상태에서 지연로딩 문제를 해결하기 위해선?

문제점

  • 준영속 상태의 엔티티를 지연로딩으로 연관 엔티티를 조회하거나
  • 변경을 하게 된다면 감지를 못해서 에러가 나거나 원하는 결과를 가질 수 없다.

해결방법

  • 영속성 컨텍스트가 종료되기 전에 즉시 로딩
    • 글로벌 패치 전략 수정(지연 → 즉시)
    • JPQL 페치 조인
    • 강제로 초기화
    • FACADE 계층 추가
  • OSIV를 사용하여 엔티티를 항상 영속 상태로 유지

글로벌 패치 전략

  • 지연로딩을 → 즉시로딩으로 수정함으로써 엔티티를 로딩할 때 바로 로딩되게끔
  • 문제점
    • 유연하지 못한 로딩 전략
      • 다른 코드에서는 연관 엔티티를 사용안 할수도 있음
    • N+1 문제

JPQL 페치 조인

  • 가장 많이 쓰는거 같음
  • 한번의 쿼리로 연관된 엔티티 한번의 조회
  • JPQL 페치 조인으로 N+1 문제 해결
  • 문제점
    • 엔티티 종류마다 메소드를 만들어서 repository 의존도가 상승할 수 있음

강제로 초기화

  • 지연로딩인 엔티티를 강제로 초기화 하여 로딩되게 함
  • 문제점
    • 글로벌 패치 전략과 동일
    • 코드가 지저분해질 수도?

FACADE 계층 추가

  • 위에 강제로 초기화 처럼 엔티티를 초기화하는 코드를 사용하다보면 Service 계층의 역할이 SRP 이 잘 구현하기 힘들다
  • 그래서 엔티티를 초기화하는 Facade 계층을 추가하는 방법이 있다.
  • 하지만 이는 코드의 역할 분리에는 좋지만 코드가 증가하고 계층에 분리로 인한 가독성이 떨어질 수 있다.

OSIV (Open Session In View)

  • JPA에서는 OEIV 라고 부르기도 한다(OSIV는 하이버네이트에서 부른다)
  • OSIV의 목적은 영속성 컨텍스트를 프레젠테이션 계층까지 열어두는 것이다.
    • 즉, Service → Repository 계층에만 국한되어 있지않게 끔 하는 것

과거 OSIV: 요청 당 트랜잭션

  • 과거 OSIV 는 요청당 스레드를 할당하면서 영속성 컨텍스트와 트랜잭션를 생성했다.
  • 따라서 Transaction per Request라고 부르기도 한다.
  • 문제점
    • 프레젠테이션 계층에서 엔티티 변경 위험
      • 엔티티 변경을 위한 해결방안
        • 엔티티를 읽기 전용 인터페이스로 제공
        • 엔티티 레핑
        • DTO만 반환
      • 하지만 위와 같은 해결방안은 해결방안이지만 해결하기 위한 부수적인 요소가 많아 사실상 과거 OSIV 는 안좋은 요소가 많다.

스프링 OSIV: 비즈니스 계층 트랜잭션

  • 사용자의 요청에 영속성 컨텍스트를 생성한다.
  • 그리고 트랜잭션을 생성 ~ 종료 와 영속성 컨텍스트의 생존과는 무관하다.
  • 요청이 끝날 때 영속성 컨텍스트를 종료한다.
  • 특징
    • 영속성 컨텍스트를 프리젠테이션 계층까지 유지한다.
    • 프리젠테이션 계층에는 트랜잭션이 없으므로 엔티티를 수정할 수 없다.
    • 프리젠테이션 계층에는 트랜잭션 없이 읽기를 사용해서 지연 로딩을 할 수 있다.

트랜잭션 없이 읽기

  • 영속성 컨텍스트의 주기는 요청의 주기와 같은데, 이 때 지연로딩이 트랜잭션 없이도 가능하다.
  • 업데이트나 저장 같은 쿼리는 트랜잭션이 필요하다 하지만, 읽기 같은 조회는 트랜잭션 없이도 가능하기 때문에 가능하다!

주의 사항

  • OSIV를 사용하면 영속성 컨텍스트는 요청의 주기와 같다
  • 그렇다는 건 DataSource를 요청이 처리할 동안 쓰기 때문에 요청이 많은 서비스이면 장애가 발생하기 쉽다.
  • DB 접근을 안하는 요청에도 자원을 쓸것 같아서 흠… 쓰기 애매한거 같다.

사용법

spring.jpa.open-in-view:true #디폴트 세팅

사용하지 않을 때는 false로 해두어야 한다.

 

 

 

참고자료

- >자바 ORM 표준 JPA 프로그래밍 - 김영한

'백엔드 > JPA' 카테고리의 다른 글

[JPA] JPA N+1 문제 발생 원인과 해결 방법  (0) 2024.05.14
[JPA] 엔티티 그래프  (0) 2024.05.10