Application/JPA

[JPA] 트랜잭션 범위와 영속성 컨텍스트, Fascade 계층

반응형
JPA에 대해서 헷갈렸던 개념들을 위주로 정리하는 글입니다.

자바 ORM 표준 JPA 프로그래밍

스프링 IoC 컨테이너 환경에서 JPA가 동작하는 내부 동작 방식을 이해하고, 컨테이너 환경에서 웹 애플리케이션을 개발할 때 발생할 수 있는 다양한 문제점과 해결 방안을 알아보자.

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

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

1. 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.

즉, 트랜잭션 범위와 영속성 컨텍스트의 생존 범위가 같다는 뜻으로 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트틀 종료한다.

트랜잭션을 커밋하면 JPA는 먼저 영속성 컨텍스트를 플러시해서 변경 내용을 데이터베이스에 반영한 후에 데이터베이스 트랜잭션을 커밋한다. (만약 예외가 발생해서 트랜잭션을 롤백하고 종료하면 플러시를 호출하지 않음)

 

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

 

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

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

스프링 컨테이너는 스레드마다 각각 다른 트랜잭션을 할당한다. 따라서 같은 엔티티 매니저를 호출해도 접근하는 영속성 컨텍스트가 다르므로 멀티스레드 환경에 안전함.

 

준영속 상태와 지연 로딩

트랜잭션 범위의 영속성 컨텍스트 전략을 사용하면 트랜잭션이 없는 프레젠테이션 계층(Controller)에서 엔티티는 영속 상태가 아닌 준영속 상태이다. 따라서 변경 감지와 지연 로딩이 동작하지 않는다.

 

준영속 상태의 문제는 지연 로딩 기능이 동작하지 않는 점이다. 

예를 들어 뷰를 렌더링 할 때 연관된 엔티티도 함께 조회했다고 가정했을 때, 아직 초기화하지 않은 프록시 객체를 사용하면 실제 데이터를 불러오려고 초기화를 시도하지만 준영속 상태이므로 지연 로딩을 할 수 없다,

 

해결 방법

뷰가 필요한 엔티티를 미리 로딩해 두는 방법

OSIV를 사용해서 엔티티를 항상 영속 상태로 유지하는 방법

 

뷰가 필요한 엔티티를 미리 로딩해 두는 방법

- 글로벌 페치 전략 수정 (즉시 로딩)

- JPQL 페치 조인

- 강제로 초기화

 

1. 글로벌 페치 전략 수정 (즉시 로딩)

@ManyToOne(fetch = FetchType.EAGER)

따라서 엔티티 매니저로 엔티티를 조회하면 연관된 엔티티도 함께 로딩한다.

하지만 이 방법의 단점으로는 다음과 같다.

 

- 사용하지 않는 엔티티를 로딩하는 문제
  (연관된 엔티티가 필요 없는데도 즉시 로딩 전략으로 인해 다 같이 조회)

- N + 1문제가 발생한다.
  (JPA가 JPQL를 분석해서 SQL을 생성할 때는 글로벌 페치 전략을 참고하지 않고 오직 JPQL 자체만 사용해서)

- 글로벌 페치 전략을 즉시 로딩으로 설정하면 애플리케이션 전체에 영향을 줘 너무 비 효율적이다.

 

2. JPQL 페치 조인

JPQL을 호출하는 시점에 함께 로딩할 엔티티를 선택할 수 있는 페치 조인을 사용.

글로벌 페치 전략과 페치 조인 관련 자세한 내용은 아래 참조
 

[JPA] 지연 로딩 N+1 문제와, JPQL 페치 조인

JPA에 대해서 헷갈렸던 개념들을 위주로 정리하는 글입니다. 페치 조인 페치(fetch) 조인은 SQL 조인의 종류가 아니라 JPQL에서 성능 최적화를 위해 제공하는 기능. # JPQL SELECT m FROM Member m JOIN FETCH m..

willseungh0.tistory.com

단점)

무분별하게 사용하면 화면에 맞춘 리포지토리 메소드가 증가할 수 있다. (적당한 선에서 타협점을 보자)

 

3. 강제로 초기화

글로벌 페치 전략을 지연 로딩으로 설정하면 연관된 엔티티가 실제 엔티티가 아닌 프록시 객체를 조회한다.

프록시 객체는 실제 사용하는 시점에 초기화 된다.

 

프록시 객체, 지연 로딩에 관련 자세한 내용은 아래 참조
 

[JPA] 프록시란? 지연로딩 vs 즉시 로딩란?

JPA에 대해서 헷갈렸던 개념들을 위주로 정리하는 글입니다. 프록시란? 객체는 객체 그래프를 통해 연관된 객체들을 탐색한다. 그런데 객체가 데이터베이스에 저장되어 있으므로 연관된 객체를

willseungh0.tistory.com

단점

- 프록시를 초기화하는 역할을 서비스 계층이 담당하면 뷰가 필요한 엔티티에 따라 서비스 계층의 로직을 변경해야 함.

- 따라서 프레젠테이션 계층이 서비스 계층을 침범하는 상황이 발생.

 

=> 서비스 계층에서 프레젠테이션 계층을 위한 프록시 초기화 역할을 분리해야 한다. => FACADE 계층

 

FASCADE 계층

- 프레젠테이션 계층과 도메이 모델 계층 간의 논리적 의존성을 분리.

- 뷰를 위한 프록시 객체의 초기화는 이 곳에서 담당한다.

- 서비스 계층을 호출해서 비즈니스 로직을 실행.

- 리포지토리를 직접 호출해서 뷰가 요구하는 엔티티를 찾는다.

 

하지만, 최대 단점으로는 프레젠테이션 계층과 서비스 계층 사이에 하나의 계층이 더 끼어든다는 점이다.

 

 

이러한 문제점을 개선하기 위해 OSIV 방식이 있는데 이는 다음 글에서 설명하겠습니다.

 

반응형