영속성 컨텍스트가 어떻게 Entity 객체를 효율적으로 관리하고 있는지 기능을
- 1차 캐시
- 쓰기 지연 저장소
- 변경 감지
순으로 소개하겠다.
Test 작성 전 `EntityManagerFactory`와 `EntityManger` 세팅 ✔️
EntityManagerFactory emf;
EntityManager em;
@BeforeEach
void setUp() {
emf = Persistence.createEntityManagerFactory("memo");
em = emf.createEntityManager();
}
1차 캐시
영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있는데, 우리가 저장하는 Entity 객체들이 이 캐시 저장소(1차 캐시)에 저장된다. 해당 캐시 저장소는 `Map` 자료구조 형태(key-value)로 되어있다.
Entity 저장
memo Entity 객체를 캐시 저장소에 저장하기 전✔️
em.persist(memo);
memo Entity 객체를 캐시 저장소에 저장한 후 ✔️
비어있던 `entityKeys`가 `entity.Memo#1`로 채워졌다.
Entity 조회
1. 캐시 저장소에 조회하는 ID가 존재하지 않은 경우
a. 캐시 저장소 조회
em.find(Memo.class, 1);
b. DB `SELECT` 조회 후 해당 값을 캐시 저장소에 저장하고 반환
2. 캐시 저장소에 조회하는 ID가 존재하는 경우
a. 캐시 저장소 조회
b. 값이 있다면 해당 Entity 객체 반환
1차 캐시 사용의 장점
1. DB 조회 횟수를 줄임
2. 객체 동일성 보장: '1차 캐시'를 사용해 DB `row` 1개 당 객체 1개가 사용되는 것을 보장
(한 row당 객체는 하나다.)
Entity 삭제
현재 entity는 `managed` 상태이다. 즉 영속성 컨텍스트로부터 관리받고 있는 상태를 뜻한다.
em.remove(memo);
삭제할 Entity를 `MANAGED`에서 `DELETE` 상태로 만들고, 트랜잭션 `commit` 후에 `Delete` SQL이 DB에 요청된다.
Hibernate:
/* delete com.sparta.entity.Memo */ delete
from
memo
where
id=?
`commit`후에 `delete sql`문이 실행되고 아래와 같이 모두 `null`이 된다.
쓰기 지연 저장소(ActionQueue) ✨✨
JPA는 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션 `commit` 후 한번에 DB에 반영한다.
TEST진행: 두개의 객체를 insert한 후 - 현재 commit 전✔️
TEST진행: 두개의 객체를 insert한 후 - 현재 commit 후✔️
commit 후 SQL문이 실행된다.
트랜잭션 commit 전
-- et.commit
Hibernate:
/* insert com.sparta.entity.Memo
*/ insert
into
memo (contents, username, id)
values
(?, ?, ?)
Hibernate:
/* insert com.sparta.entity.Memo
*/ insert
into
memo (contents, username, id)
values
(?, ?, ?)
트랜잭션 commit 후
+ `em.flush()`;
영속성 컨텍스트의 변경된 내용들을 db에 요청하는 메서드이다. commit전에 flush를 써버리면 바로 반영이 된다.
flush() 전
-- em.flush();
Hibernate:
insert
into
memo (contents, username, id)
values
(?, ?, ?)
flush() 후
트랜잭션 commit 전
-- et.commit();
트랜잭션 commit 후
중간 정리❗
만약 EntityTransaction 환경을 끈다면, no transaction is in progress 에러가 뜬다.
`insert, update, delete`같이 데이터를 변경하는 `sql`를 `db`에 요청 및 반영하기 위해서는 transaction환경이 필요하다.
select는 상관이 없다~. 필요할 때가 있지만 이후에 다룰 내용.
변경 감지(Dirty Checking) ⚠️⚠️
JPA는 `update`메서드를 지원하지 않는다. why? 하나의 Update SQL로 처리할 수 있는 상황을 여러번 Update SQL을 요청하게 되기 때문에 비효율적이다. 그렇다면 how? jpa가 update를 처리하는 방법이 무엇일까??
먼저 아래는 새 객체를 만들어서 값을 저장(insert)한 후 이름과 내용을 수정하고 난 상태이다.
`entityInstance`(현재상태)와 `loadedState`(최초상태)를 비교해본다.
1. 비교했는데 값이 다르다면(변경이 되었다면)!
2. update SQL 생성하고
3. 쓰기지연저장소에 저장,
4. 담겨있는 모든 쓰기지연저장소에 있는 sql을 db에 요청,
5. db에 transaction이 commit되면서 반영이 된다.
정리하자면 따로 update는 할 필요없이 그저 set메서드로 내용을 변경을 하면 위의 과정을 통해 자동으로 반영이 된다.