웹 애플리케이션과 영속성 관리

  • 스프링 환경에서 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 주의사항
  • 프리젠테이션 계층에서 엔티티를 수정후 트랜잭션을 시작하는 서비스계층을 호출하는 경우
    • 프리젠테이션 계층에서 변경한 엔티티가 서비스계층이 끝날때 플러시됨

너무 엄격한 계층