1. 영속성 컨텍스트란?
영속성 컨텍스트는 "엔티티를 영구 저장하는 환경"을 의미한다.
영속성 컨텍스트는 '논리적인 개념' 이다.
Entity Manager를 통해 영속성 컨텍스트에 접근 한다.
2. 엔티티의 상태
1. 비영속 (new / transient)
2. 영속 (managed)
3. 준영속 (detached)
4. 삭제 (removed)
3. 영속성 컨텍스트의 이점
[영속성 컨텍스트의 구성요소]
[3-1. 1차 캐시]
로그를 보면, 순서가 이상한 것을 볼 수 있다.
코드를 보면 아래의 순서로 출력되어야 할 것 같다.
1. member 객체 생성 -> 출력 X
2. "==== BEFORE ====" 출력
3. DB 저장 쿼리 출력 (insert 문)
4. "==== AFTER ====" 출력
5. DB 조회 쿼리 출력 (select 문)
6. findMember 관련 정보 출력 (findMember, findMember.Id, findMember.Name)
그러나 실제 출력된 로그 순서는 아래와 같다.
2. "==== BEFORE ====" 출력
4. "==== AFTER ====" 출력
6. findMember 관련 정보 출력 (findMember, findMember.Id, findMember.Name)
5. DB 조회 쿼리 출력 (select 문)
심지어 " 5. DB 조회 쿼리 출력 (select 문) "는 출력되어지지도 않았다.
실제 작동 과정은 다음과 같다.
1. em.persist(member) 의 의미
'BEFORE 문'과 'AFTER 문' 사이에 "em.persist(member)" 에 대한 로그가 출력되어야 할 것 같지만,
em.persist(member) 는 DB에 member 를 저장한다는 의미가 아니다.
이는 member 객체를 영속성 컨텍스트의 1차 캐시에 저장한다는 의미이다.
그러므로, SQL 쿼리가 DB로 전송되는 일은 아직 일어나지 않는다.
<현재 출력 로그>
==== BEFORE ====
==== AFTER ====
2. em.find(Entity) 와 DB에서의 조회
em.find() 를 통해, 객체를 조회했는데, DB에 SQL 조회 쿼리가 로그에 출력되지 않았다.
그 이유는 영속성 컨텍스트의 1차 캐시에 해당 객체가 존재하기 때문이다.
이 때문에, 1차 캐시에서 곧바로 해당 객체를 반환 할 수 있다.
만약, 1차 캐시에 원하는 객체가 없다면?
1. 1차 캐시를 조회한다.
2. 1차 캐시에 원하는 객체가 없다면, DB를 조회한다.
3. DB에서 원하는 객체를 발견한다면, 이를 1차 캐시에 저장한다.
4. 1차 캐시에서 객체를 반환한다.
순으로 동작하게 된다.
따라서, SQL 조회 쿼리는 출력되지 않는다.
<현재 출력 로그>
==== BEFORE ====
==== AFTER ====
3. 객체 정보 출력
앞서, 1차 캐시에서 찾은 객체의 정보를 출력한다.
<현재 출력 로그>
==== BEFORE ====
==== AFTER ====
findMember 관련 정보 출력(findMember, findMember.Id, findMember.Name)
4. "DB 저장 쿼리 출력 (insert 문)" 이게 왜 지금 되지?
em.persist()에서 DB 저장 SQL이 출력되지 않은 이유는 "1차 캐시에 저장되기 때문이다." 라고 설명했다.
그런데 왜 지금 저장 SQL 쿼리가 출력된 것 일까?
그 이유는 "트랜잭션이 커밋" 때문이다.
JPA의 모든 데이터 조작은 해당 트랜잭션 안에서 이루어진다.
트랜잭션이 커밋되는 순간 모든 데이터 조작은 종료되고, 반영된 조작들을 확정하기 위해 DB에 쿼리를 보내게 된다.
따라서, 1차 캐시에 저장되었던, 객체는 DB에 insert 쿼리를 보냄으로서, DB에 저장된다.
(1차 캐시는 해당 트랜잭션안에서만 동작한다. 초기화)
<완료된 출력 로그>
==== BEFORE ====
==== AFTER ====
findMember 관련 정보 출력(findMember, findMember.Id, findMember.Name)
DB 저장 쿼리 출력 (insert 문)
<DB에 저장된 객체>
1차 캐시를 가짐으로서의 장점 : "동일성 (identity) 보장"
한 트랜잭션 속에서 같은 엔티티를 반복 조회 한다면, 같은 객체를 반환한다. (동일성 보장)
<현재 DB에 저장된 객체>
1. 첫번째 엔티티 조회
=> 현재 영속성 컨텍스트의 1차 캐시는 해당 엔티티를 가지고 있지 않기 때문에, DB에 쿼리를 보내서 1차 캐시로 엔티티를 가져오고 1차 캐시에서 반환한다.
2. 두번째 엔티티 조회
=> 두번째 요청시에는 이미 '1차 캐시' 에 엔티티가 존재하고 있기 때문에, 그대로 반환한다.
'첫번째 요청' 시에만 SQL 쿼리가 발송된 것을 볼 수 있다.
가장 중요한 점은 "한 트랜잭션" 안에서만 가능하다는 것이다.
[3-2. 쓰기 지연 SQL 저장소]
쓰기 지연 SQL 저장소를 가짐으로서의 특징은
1. 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
2. 변경 감지 (Dirty Checking)
3. 지연 로딩 (Lazy Loading)
이 있다.
쓰기 지연 SQL 저장소를 가짐으로서의 특징 : 1. 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
"=============" 가 출력되기전, 등록 SQL 문은 반환되지 않는다.
즉, "em.persist() 가 작동되는것은 DB에 저장하는 것이 아니고, 영속성 컨텍스트에 관리된다." 라는 것을 우리는 알고있다.
트랜잭션 이 commit 되는 시점에 DB로 저장된다고 알고 있다.
따라서, 위와 같은 로그가 출력된다.
이를 "쓰기 지연"이라고 한다.
'영속성 컨텍스트'는 '쓰기 지연 SQL 저장소' 를 통해 이를 보장한다.
이 시기에 엔티티를 등록하는 SQL 문을 '쓰기 지연 SQL 저장소'에 모아 두었다가.
이 시점에 모아 놓은 SQL 문을 모두 DB로 반환 시킨다.
(+ 트랜잭션이 커밋된 시점에, 영속성 컨텍스트의 1차 캐시는 초기화 된다.)
쓰기 지연 SQL 저장소를 가짐으로서의 특징 : 2. 변경 감지 (Dirty Checking)
한 트랜잭션 안에서 엔티티에 대한 내용 변경이 일어난 경우,
트랜잭션이 커밋 되었을때, 1차 캐시에 엔티티와 스냅샷을 비교하여 변경된 부분을 UPDATE 쿼리를 이용하여 DB에 반영한다.
스냅샷은 한 엔티티가 영속성 컨텍스트에 들어온 순간의 상태 (내용)을 기록해 놓은 것이다. (스냅샷 떠놓음.)
쓰기 지연 SQL 저장소를 가짐으로서의 특징 : 3. 지연 로딩 (Lazy Loading)
'Spring > JPA' 카테고리의 다른 글
[JPA - 6] 연관관계 매핑 (양방향 연관관계 / 연관관계의 주인 - 중점) (0) | 2023.11.26 |
---|---|
[JPA - 5] Entity Mapping (0) | 2023.11.25 |
[JPA - 3] JPA 구동 방식 / 간단한 JPA를 이용한 데이터 조작 (1) | 2023.11.20 |
[JPA - 2] JPA 설정파일 : "persistence.xml" (0) | 2023.11.19 |
[JPA - 1] JPA 시작하기 - Setting (+ Spring Boot ver 에 적합한 라이브러리 ver 찾는 방법 ) (0) | 2023.11.19 |