DevKim

[JPA] 영속성 컨텍스트 (Persistence Context) - 1차 캐시,쓰기 지연, 변경 감지 본문

JPA

[JPA] 영속성 컨텍스트 (Persistence Context) - 1차 캐시,쓰기 지연, 변경 감지

on_doing 2021. 5. 20. 17:10
728x90

[ Entity Manager & Factory ]

영속성 컨텍스트에 들어가기 전에, 엔티티 매니저 팩토리와 엔티티 매니저 동작과정을 간단하게 정리해보자

1.고객의 요청이 올때마다 팩토리가 EntityManager을 생성하고

2. EntityManager은 내부적으로 데이터 connection을 사용해서 DB를 사용하게된다.


[ 영속성 컨텍스트란? ]

"엔티티를 영구 저장하는 환경"이라는 뜻이다.

-> DB에 저장하는게 아니라, entity를 영속성 context에 저장한다는 뜻

-> EntityManager.persist(entity);

 

영속성 컨텍스트는 논리적인 개념이기에 당연히 눈에 보이지 않는다.

 

엔티티매니저를 통해 영속성 컨텍스트에 접근한다.

풀어서 이해해보면, 엔티티 매니저가 생성될 때

그 안에 1:1로 영속성 컨텍스트라는 보이지 않는 공간이 생성이 된다는 것이다.

 

기본 J2SE 환경에서는 엔티티 매니저와 영속성 컨텍스트가 1:1로 생성되지만,

Spring 프레임워크같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1로 생성이된다.

 

 


[ 영속성 컨텍스트가 필요한 이유 = 이점 ]

<1> 1차캐시

<2> 동일성(idenity) 보장

<3> 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)

<4> 변경감지(Dirty checking)

<5> 지연 로딩(Lazy loading)


[1] 1차 캐시

내부적으로 보면, 영속성 컨텍스트는 내부에 1차 캐시를 들고 있다고한다.

만약 비영속 상태인 member이라는 객체를 em.persist(member)로 영속화를 시키면

내부적으로 1차 캐시에 저장되게된다.

 

em.find(Member.class,"member1")로 id가 member1인 객체를 find 날리면

JPA는 DB가 아닌 안에 있는 1차 캐시를 뒤져서(?), 있으면 캐시에 있는 값을 그냥 조회해버린다.

 

만약, id="member2"라는 애는 DB에는 저장되어있으나, 1차 캐시에는 없다고 가정을 해보자.

find("member2")를 날리면 먼저 1차 캐시를 탐색한다 -> 1차 캐시에 없으면 DB를 조화한다 -> 이때 DB에 저장되어있으면 id="member2"인 데이터를 1차 캐시에 저장을 하고 반환한다.

 

******

 사실 이것이 엄청난 성능 이점을 주진 않는다. 왜냐면 entitymanager이라는건 트랜잭션 단위로 만들고, DB 트랜잭션이 끝날 때 같이 종료 시켜버린다. 즉 고객의 요청이 들어와서 비즈니스가 끝나버리면 영속성 context를 지우므로 1차 캐시도 지워진다. DB의 한 트랜잭션 안에서만 효과가 있기 때문에 이것으로 인해 엄청난 성능적인 이점을 얻을 수 있는건 아니라고한다. 다만 성능보다는 매커니즘을 통해 얻을 수 있는 이점이 있다.

*******


[2] 영속 엔티티의 동일성 보장

같은 트랜잭션 안에서라고 가정했을 때, a와 b는 동일하다

1차 캐시로 반복 읽기 가능한 읽기 등급의 트랜잭션 격리 수준을 애플리케이션 차원에서 제공한다.

Member a= em.find(Member.class,"member1");
Member b= em.find(Member.class,"member1");

a==b //true

[3] 트랜잭션을 지원하는 쓰기 지연

= 버퍼 기능을 얻을 수 있다.

Persistence.xml의 jdbc.batch_size 에서 설정한만큼 쌓였다가 한꺼번에 DB에 날려줄 수 있다.

entitymanager에는 1차캐시와 더불어서 쓰기 지연 SQL 저장소도 존재하는데, 여기에 쿼리를 차곡차곡 쌓아뒀다가,

commit이 날라오면 (transaction.commit()) flush가 발생하면서 영속성 컨텍스트의 변경내용이 DB에 반영이 된다.


[4] Dirty checking (변경 감지)

엔티티 수정시, member.setName("홍길동") 으로 값만 바꿔줘도 log에 update 쿼리가 자동으로 나가는 것을 볼 수 있다.

평소에는 그냥 그렇구나~ 했는데 em.update와 같은 코드가 없어도 변경 쿼리가 어떻게 자동으로 나가는걸까?

그 이유 또한 영속 컨텍스트에 있다!

 

영속 컨텍스트 안에 있는 1차 캐시에는 Id,Entity 외에도 스냅샷이라는 정보가 존재한다.

스냅샷은 값을 읽어온 최초 시점을 스냅샷으로 떠뒀다고 생각하면 된다.

 

Flush가 되는 시점에, JPA는 스냅샷의 값과 현재 등록된 Entity값을 쭉~ 비교한다.

이때 뭔가 변경을 감지하면, UPDATE SQL을 생성해서 쓰기 지연 저장소에 쌓아둔다.

그렇게 UPDATE 쿼리를 DB에 반영하고, commit하게 되는 것이다.

 

 

그림 출처 : 자바 ORM 표준 JPA 프로그래밍

 

 

728x90
Comments