[JPA] 웹 애플리케이션과 영속성 관리
들어가며
해당 글은 자바 ORM 표준 프로그래밍을 정리한 글입니다.
트랜잭션 범위의 영속성 컨텍스트
- 보통 Service 계층에서 @Transaction 어노테이션을 선언해서 트랜잭션을 시작한다.
- 스프링 트랜잭션 AOP는 대상 메소드 호출하기 직전에 트랜잭션을 시작하고, 대상 메소드가 정상 종료되면 트랜잭션을 커밋하면서 종료한다.
- 트랜잭션을 커밋하면 JPA는 먼저 영속성 컨텍스트를 플러시해서 변경 내용을 데이터베이스에 반영한 후에 데이터베이스 트랜잭션을 커밋한다.
- 트랜잭션이 같으면 같은 영속성 컨텍스트를 사용한다.
- 같은 엔티티 매니저를 사용하더라도 트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다.
준영속 상태와 지연 로딩
- 스프링이나 J2EE 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용하므로 서비스와 레포지토리 계층에서는 영속성 컨텍스트에 관리되면서 영속 상태를 유지하지만 컨트롤러나 뷰 같은 프리젠테이션 계층에서는 준영속 상태가 된다. 따라서 변경감지와 지연 로딩이 동작하지 않는다.
준영속 상태의 지연 로딩 문제 해결 방법
- 뷰가 필요한 엔티티를 미리 로딩해두는 방법
- 글로벌 페치 전략 수정
- JPQL 페치 조인
- 강제로 초기화
- OSIV를 사용해서 엔티티를 항상 영속 상태로 유지하는 방법
글로벌 페치 전략 수정
- fetch type을 LAZY → EAGER로 변경해서 글로벌 페치 전략을 수정한다.
글로벌 페치 전략에 즉시 로딩 사용시 단점
- 사용하지 않는 엔티티를 로딩한다.
- N+1 문제가 발생한다.
- JPQL을 이용해서 list 단위로 조회하는 경우 글로벌 페치전략을 참고하지 않고 오직 JPQL 자체만 사용하기에 1번의 전체 조회 후 조회된 N개에 대해서 select하는 N번의 SQL이 실행 된다.
JPQL 페치 조인
- 페치 조인은 조인 명령어 마지막에 fetch를 넣어 주면 SQL JOIN을 사용해서 페치 조인 대상까지 함께 조회하기에 N+1 문제가 발생하지 않는다.
JPQL 페치 조인의 단점
- 필요한 데이터에 맞게 페치 조인 메소드를 만들던지 전체 페치조인하는 메소드를 사용해서 다른 필드를 사용하지 않는 방법을 사용해야 하기에 사용하는 곳에서 타협점을 찾아야 한다.
강제로 초기화
- 프록시 객체로 조회한 엔티티를 서비스 계층에서 초기화하여 프레젠테이션 계층에서 초기화된 엔티티를 넘겨주는 방식
- FACADE 계층
- 프록시 계층에 데이터를 초기화 하는 계층으로 프레젠테이션과 서비스 계층의 중간 단계라고 생각하면 된다.
- 의존성을 분리할 수 있지만 더 많은 코드를 작성해야 한다.
OSIV
- Open Session In View는 영속성 컨텍스트를 뷰까지 열어둔다는 뜻이다. JPA에서는 OEIV(Open EntityManager In View)라 한다.
과거 OSIV: 요청 당 트랜잭션
- 클라이언트 요청이 들어오자마자 서블릿 필터나 스프링 인터셉터에서 트랜잭션을 시작하고 요청이 끝날 때 트랜잭션도 끝내는 방식
요청 당 트랜잭션 방식의 OSIV 문제점
- 컨트롤러나 뷰 같은 프리젠테이션 계층이 엔티티를 변경할 수 있다는 점이다.
프리젠테이션 계층에서 엔티티를 수정하지 못하게 막는 방법
- 엔티티를 읽기 전용 인터페이스로 제공
- 인터페이스의 View를 상속해서 getter만 갖고 있는 인터페이스를 프레젠테이션에 넘겨주는 방식
- 엔티티 레핑
- 엔티티 객체를 멤버 변수로 갖고 있는 Wrapper 객체를 만들어 읽기 전용 메소드만 제공
- DTO만 반환(가장 많이 사용)
- Data Transfer Object를 만들어서 프레젠테이션 계층에 전달하는 방식
- 필드 값 복사 과정이 필요
- 엔티티를 수정 하지 못하게 막는 방법은 코드량이 상당히 증가하는 단점이 있다.
스프링 OSIV: 비지니스 계층 트랜잭션
- 스프링 프레임워크가 제공하는 OSIV, 필터, 인터셉터에서 적용할지에 따라 원하는 클래스를 제공
- 영속성 컨텍스트의 생존 범위는 요청이 들어올 때 부터 요청이 종료될 때까지 생존하나 수정이 가능한 시점은 서비스 계층, 트랜잭션 범위에서만 엔티티 수정가능 하다
- 엔티티를 변경하지 않고 단순히 조회만 할 때는 트랜잭션이 없어도 되는데 이것을 트랜잭션 없이 읽기(Nontransactional reads)라 한다.
특징
- 영속성 컨텍스트를 프리젠테이션 계층까지 유지
- 프리젠테이션 계층에는 트랜잭션이 없으므로 엔티티를 수정할 수 없다.
- 프리젠테이션 계층에는 트랜잭션이 없지만 트랜잭션 없이 읽기를 사용해서 지연 로딩을 할 수 있다.
스프링 OSIV 주의사항
- 프레젠테이션 계층에서 서비스 계층 호출 후 엔티티를 수정하고 다시 서비스 계층을 호출하게 되면 영속 상태의 객체들이 플러시 되어 데이터가 반영되는 문제가 있다.
- 스프링 OSIV는 같은 영속성 컨텍스트를 여러 트랜잭션이 공유 할 수 있으므로 주의가 필요
너무 엄격한 계층
- OSIV를 사용하게 되면 영속성 컨텍스트를 공유하기에 컨트롤러에서 레포지토리를 직접 호출해도 문제가 되지 않는다. 따라서 OSIV를 사용하면 좀 더 유연하고 실용적인 관점으로 접근하는 것도 좋은 방법이라 생각한다.
- JPA 리뷰 글 보러가기
Leave a comment