본문 바로가기
JPA/JPA원리

3 JPA 영속성 컨텍스트 이해하기

by 킹차니 2021. 7. 3.
김영한님의 인프런 강의와 PDF를 바탕으로 정리하였습니다.
https://www.inflearn.com/courses?s=%EA%B9%80%EC%98%81%ED%95%9C

 

 

 

영속성 컨텍스트엔티티를 영구 저장하는 환경이라는 의미로, 눈에 보이지 않는 논리적인 개념입니다.

영속성 컨텍스트 접근하기 위해서는 EntityManagerFactoryEntityManager를 통해 접근해야 합니다.

 

EntityManager,  EntityManagerFactory

 

 

엔티티의 생명주기

엔티티의 생명 주기에는 4가지 단계가 있습니다.

1. 비영속( new / transient ) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태 (ex. A객체를 new했을 때)

2. 영속( managed ): 영속성 컨텍스트에 관리되는 상태 (ex. A객체를 persist한 상태)

3. 준영속( detached ): 영속성 컨텍스트에 저장되었다가 분리된상태 (ex. A객체를 영속성 컨텍스트에서 삭제한 상태)

4. 삭제( removed ): 삭제된 상태 (ex. A객체를 DB에서 삭제한 상태)

 

 

엔티티의 생명주기를 그림으로 나타내면 아래와 같습니다.

엔티티 생명주기

 

 

 

이제 각각의 엔티티 생명주기 4단계를 그림과 코드로 보겠습니다.

 

1. 비영속(new/transient) 

 

2. 영속(managed)

 

비영속과 영속 상태

 

 

3. 준영속(detached)

영속성 컨텍스트에서 삭제

 

4. 삭제(removed)

DB에서 ID=100인 member 삭제.

 

 

 

 

영속성 컨텍스트를 사용하면  몇가지 이점이 있습니다.

영속성 컨텍스트의 이점

1. 1차캐시

2. 동일성(identify) 보장

3. 트랜잭션을 지원하는 쓰기 지원(transactional write-behind)

4. 변경감지(Dirty Checking)

5. 지연 로딩(Lazy Loading)

 

 

 

1. 1차 캐시

 

쉽게 말하면 하나의 트랜잭션 안에서 데이터 조회시 1차 캐시에서 먼저 찾아보고 있다면 1차 캐시에서 찾은 엔티티를 넘겨주고, 없다면 DB로 가서 찾아봅니다. DB에서 찾았다면 해당 데이터를 1차 캐시에 저장하고 넘겨줍니다.

 

아래의 코드처럼 객체를 생성하고 해당 객체를 persist하면 1차 캐시에 저장됩니다.

1차 캐시에 저장된 member1

 

1차 캐시에 저장했으니 조회해보겠습니다.

실행결과:

결과를 보면 분명 조회(find)를 했는데 select쿼리가 날라가지 않은 것을 볼 수 있습니다.

이는 1차 캐시에서 찾아왔기 때문입니다.

 

 

하지만 1차 캐시에 없다면 DB에서 조회한 후 다시 1차 캐시에 저장합니다.

실행결과:

select문 한 번만 수행

select문을 한번만 수행한 이유는 find를 할 때, 먼저 1차캐시에 없던 member(id=101)를 DB에서 조회하여 찾아온 뒤, 해당 member(id=101)를 1차캐시에 올려두고, 

다음 find에서는 1차캐시에 있던 member(id=101)를 가져올 수 있어서 입니다. 아래 그림과 같습니다.

 

 

 

 

 

2. 동일성(identify) 보장

 

자바 컬렉션에서 같은 객체를 꺼낸 것은 같은 래퍼런스를 가지는 것 처럼 JPA의 영속성 컨텍스트도 이와 같은 동일성을 보장한다는 것입니다.

실행결과:

결과가 true가 나오는 것을 보아 동일성을 보장하는 것을 알 수 있습니다.

 

즉 1차 캐시로 반복 가능한 읽기(repeatable read)등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공합니다.

 

 

 

 

 

3. 트랜잭션을 지원하는 쓰기 지원(transactional write-behind)

 

commit전까지 sql문을 날리지 않고, 엔티티는 1차 캐시에 저장하고, sql문은 "쓰기 지연 SQL저장소"에 저장해둡니다.

그리고 commit이 실행되면 해당 엔티티를  "쓰기 지연 SQL저장소"에 저장된 sql문으로 flush합니다.

실행결과:

commit시점에 쓰기지연 저장소에 모우둔 insert문을 날렸습니다.

 

그림으로 보면 아래와 같습니다.

 

이러한 트랜잭션 지원 덕에 버퍼링 사용할 수 있는 이점을 얻을 수 있습니다.

아래와 같이 persistence.xml에 배치에 대한 옵션을 추가하고, value까지 정할 수 있습니다.

 

 

4. 변경감지(Dirty Checking)

먼저 아래의 코드와 실행결과를 보자.

현재 id=102인 멤버의 이름은 아래의 테이블에서 보이듯이 "member1" 이라는 이름을 가지고 있습니다.

위의 코드는 id=102, name=member1 인 멤버를 조회하고 조회된 멤버를 자바에서 setName()을 통해 "changedMember1"로 수정하였습니다.

그런데 실행해보면 아래와 같은 결과가 나옵니다.

날린 적도 없는 update문이 실행되는 것이 보이고 , Member테이블의 ID가 102인 memberd의 이름이 수정되었습니다.

자바에서 컬렉션에 들어있는 객체를 수정하듯이 name을 바꿨는데 테이블의 update문이 수행되어 테이블의 내용도 수정된 것입니다.

 

이러한 기똥찬 일은 영속성 컨텍스트변경 감지(Dirty Checking)입니다.

아래의 그림과 같은 일이 일어납니다.

 

1. JPA에서는 조회(find)를 하면 1차 캐시에 저장하는 것에 추가로 스냅샷을 저장한다.(스냅샷은 조회했을 당시의 상태를 저장해 두는 것이다.)

2. commit시점에 스냅샷과 엔티티를 비교하여 변경된 부분이 있으면 update쿼리를 DB로 날린다.

(Dirty Checking의 dirty는 상태의 변화가 생긴 정도이다.)

 

 

5. 지연 로딩(Lazy Loading)

지연 로딩에 대한 것은 후에 알아보겠습니다.

 

 

 

 

 

 

 

 

참고자료:

김영한님 인프런 강의, 강의 PDF

향로님 블로그:https://jojoldu.tistory.com/415

'JPA > JPA원리' 카테고리의 다른 글

6 JPA 객체와 테이블 매핑, 데이터베이스 스키마 자동 생성  (0) 2021.07.04
5 JPA 준영속 상태  (0) 2021.07.03
4 JPA 플러시(flush)  (0) 2021.07.03
2 JPA로 CRUD해보기  (0) 2021.07.02
1 왜 JPA를 사용해야 하는가?  (0) 2021.07.02