영속성
- 영속성은 JPA를 이해하는데 가장 중요한 용어이다.
- 영속성이란 엔티티를 영구 저장하는 환경이라는 뜻이다.
- Application 과 DB 사이의 중간 계층
- 영속상태 구분
비영속
: 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
영속
: 영속성 컨텍스트에 관리되는 상태
준영속
: 영속성 컨텍스트에 저장되었다가 분리된 상태
- EntityManager 를 통해 영속성 컨텍스트에 접근한다.
영속성 컨텍스트의 이점
1차 캐시
- 영속성 컨텍스트 안에 1차 캐시가 존재한다.
- JPA에서 Entity 객체를 영속성 컨텍스트에 저장하거나, DB에서 특정 데이터를 조회할 때 1차 캐시 안에
@Id-Entity
가 저장된다.
- Entity를 조회할 때 1차 캐시에서 해당 Entity가 존재하는지 조회하고, 존재한다면 해당 Entity를 반환하기 때문에 성능상 이점을 얻을 수 있다.
- 하지만, 트랜잭션이 끝나면 영속성 컨텍스트를 지워버리기 때문에 성능상 이점이 매우큰 편은 아니다.
동일성 보장
- DB로부터 동일한 데이터(같은 PK의 데이터)를 꺼내어 비교할 때 논리적으로 동일하다는 것을 보장해준다.
- 그 이유는 1차 캐시에 Entity가 저장되어 사용되기 때문이다.
트랜잭션을 지원하는 쓰기지연
- 쓰기 작업에 대한 쿼리를 보내지 않고 쌓아놓다가 트랜잭션을 커밋하는 순간 데이터베이스에서 쿼리를 수행한다.
- Entity 객체를 영속할 때 1차 캐시에 Id-Entity 를 저장하고
쓰기 지연 SQL 저장소
에 쓰기 작업에 대한 쿼리를 저장한다.
- 즉, 쿼리를 버퍼링하여 한 번에 처리하기 때문에 약간의 성능 향상을 기대할 수 있다.
변경 감지 (Dirty checking)
- 변경 감지는
더티체킹(Dirty Checking)
이라고도 함
- Entity를 최초 조회한 시점에서 최초 데이터에 대한 스냅샷을 생성하여 1차 캐시에 저장한다.
- 이후 트랜잭션 커밋 이전에 스냅샷과 현재 Entity의 필드를 비교하여 변경 사항을 감지하고 이에 대한 UPDATE 쿼리를 생성하여 쓰기지연 SQL 저장소에 저장한다.
지연 로딩 (Lazy loading)
- JPA에서는 연관 Entity를 조회하는 방법 중 하나로 지연로딩을 제공함.
- 지연 로딩은 Entity와 연관관계를 맺고 있는 다른 Entity에 대한 로딩을 지연시키는 기능임
- 연관된 Entity에 대한 접근이 이루어질 때 조회하는 방식
- 연관 Entity에 대한 접근이 발생할 때까지 로딩을 미룬다는 점에서 성능상 이점을 얻을 수 있음
준영속 상태
- 준영속 상태란, 영속 상태인 엔티티가 영속성 컨텍스트에서 분리된 상태를 말함
persist()
, find()
등의 API를 통해 영속상태로 만들 수 있음
- 준영속 상태로 전환하는 방법
detach(Entity)
: 인자로 받은 엔티티를 영속성 컨텍스트에서 제외시킴
clear()
: 영속성 컨텍스트를 완전히 초기화
close()
: 영속성 컨텍스트를 닫아버림
OSIV (Open-In-Session-View)
- OSIV는 영속성 컨텍스트를 View 레벨까지 열어두는 기능이다.
- 영속성 컨텍스트가 유지되면 엔티티도 영속 상태로 유지된다.
- View 레벨까지 영속성 컨텍스트가 살아있다면 View 에서도 지연 로딩을 사용할 수 있다.
- JPA 에서는
OEIV(Open EntityManager In View)
, Hibernate 에서는 OSIV(Open In Session View)
라고 하지만 관례상 둘 다 OSIV라고 부른다.
OSIV [ON]
spring.data.open-in-view: true # 기본값
- Spring의 기본 설정 값은 OSIV → true 이다.
- 위 그림처럼, Request를 수신할 때부터 영속성 컨텍스트가 생성되고 트랜잭션 커밋 이후에도 영속성 컨텍스트가 살아있다가 영속성 컨텍스트가 생성되었던 모듈까지 응답이 return되면 영속성 컨텍스트가 종료된다.
- 중요한 점은, Service 계층에서 트랜잭션이 끝나더라도 영속성 컨텍스트가 살아있다는 점이다.
- 영속성 컨텍스트는 트랜잭션 범위 밖에서 엔티티를
조회
만 할 수 있다.
- 이를
트랜잭션 없이 읽기 (Non Transactional Reads)
라고 한다.
- 당연하게도, 트랜잭션 범위 밖에서 Entity를 수정하여도 Dirty checking 을 통한 UPDATE 쿼리가 발생하지 않는다.
em.flush()
는 트랜잭션 범위 안에서만 동작가능하기 때문
- OSIV를 활성화 시켰을 때 가장 큰 단점은 DB connection을 오래 물고있게 된다는 점이다.
- 트랜잭션 종료 이후에도 영속성 컨텍스트를 유지하기위해 DB Connection이 필요하다.
- 하나의 Thread가 DB Connection을 오래 물고있게 되면, Connection이 모자란 상황이 발생할 수 있다.
- Connection이 필요한 상황에서 connection 고갈이 발생하게 된다면 해당 Thread 는
Wating
상태에 빠지게 된다.
OSIV [OFF]
spring.data.open-in-view: false
- OSIV를 끄면 트랜잭션을 종료할 때 영속성 컨텍스트도 함께 종료한다.
- OSIV를 끄면 트랜잭션 영향권 바깥에서 지연로딩을 사용할 수 없으므로, 트랜잭션 영향권 안에서 데이터를 전부 조회해놓아야한다.