1차 캐시
엔티티를 영속성 컨텍스트에 저장할 때(em.persist()) 생성되는 메모리 안의 캐시이다. 엔티티는 먼저 1차 캐시에 저장되고 이후 같은 엔티티를 요청하면 DB에 요청하지 않고 1차 캐시에서 데이터를 반환하여 성능을 높인다.
1. 영속

- 영속성 컨텍스트의 1차 캐시에 저장된다.
// 비영속
Tutor tutor = new Tutor(1L, "병천", 100);
// 영속, 1차 캐시에 저장
em.persist(tutor);
2. 영속된 Entity 조회

- DB가 아닌 1차 캐시에 저장된 Entity를 먼저 조회한다.
public static void main(String[] args) {
// EntityManagerFactory 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
// EntityManager 생성
EntityManager em = emf.createEntityManager();
// Transaction 생성
EntityTransaction transaction = em.getTransaction();
// 트랜잭션 시작
transaction.begin();
try {
// 비영속
Tutor tutor = new Tutor(1L, "wonuk", 100);
// 영속
System.out.println("persist 전");
em.persist(tutor);
System.out.println("persist 후");
Tutor findTutor = em.find(Tutor.class, 1L);
System.out.println("findTutor.getId() = " + findTutor.getId());
System.out.println("findTutor.getName() = " + findTutor.getName());
System.out.println("findTutor.getAge() = " + findTutor.getAge());
// transaction이 commit되며 실제 SQL이 실행된다.
transaction.commit();
} catch (Exception e) {
// 실패 -> 롤백
e.printStackTrace();
transaction.rollback();
} finally {
// 엔티티 매니저 연결 종료
em.close();
}
emf.close();
}
- 1차 캐시의 Entity를 조회한다.
- 조회 SQL이 실행되지 않는다.
- 트랜잭션 Commit 시점에 INSERT SQL이 실행된다.
3. 데이터베이스에 저장된 데이터를 조회할 때

- 1차 캐시에 없으면 DB를 조회하고 그것을 1차 캐시에 저장하고 반환한다.
- 1차 캐시는 동일한 트랜잭션 안에서만 사용이 가능하다.
- 요청이 들어오고 트랜잭션이 종료되면 영속성 컨텍스트는 삭제된다.
동일성 보장
동일한 트랜잭션 안에서 특정 엔티티를 여러 번 조회해도 항상 같은 객체 인스턴스를 반환한다. 영속성 컨텍스트는 1차 캐시를 사용하여 같은 엔티티를 주복 조회해도 동일한 객체를 참조하게 하여 일관성을 유지한다.

- 동일한 트랜잭션 내에서 조회된 Entity는 같은 인스턴스를 반환한다.
- DB에 저장된 데이터를 조회하여 1차 캐시에 저장한다.
- 1차 캐시에 저장된 데이터를 조회한다.
// DB 조회, 1차 캐시에 저장
Tutor findTutor1 = em.find(Tutor.class, 1L);
// 1차 캐시 조회
Tutor findTutor2 = em.find(Tutor.class, 1L);
System.out.println("findTutor1 == findTutor2 : " + findTutor1.equals(findTutor2));
// transaction이 commit되며 실제 SQL이 실행된다.
transaction.commit();
"
findTutor1 == findTutor2 : true
"
쓰기 지연
엔티티 객체의 변경 사항을 DB에 바로 반영하지 않고 트랜잭션이 커밋될 때 한 번에 반영하는 방식으로 이를 통해 성능을 최적화하고 트랜잭션 내에서의 불필요한 DB 쓰기 작업을 최소화 한다. 동일한 트랜잭션 내에서 생성된 SQL들을 Commit 시점에 한꺼번에 반영함.

- tutor1 1차 캐시에 저장
- 쓰기 지연 저장소에 tutor1 INSERT SQL 저장

- tuor2 1차 캐시에 저장
- 쓰기 지연 저장소에 tutor2 INSERT SQL 저장

- flush 되면서 SQL 쿼리 실행(쿼리만 만들어 놓고 DB 앞에서 대기)
- 트랜잭션이 Commit 되면서 실제 DB에 반영
- 쓰기 지연이 발생하는 시점
- flush() 동작이 발생하기 전까지 최적화한다.
- flush() 동작으로 전송된 쿼리는 더이상 쿼리 최적화는 되지 않고, 이후 commit()으로 반영만 가능하다.
- 쓰기 지연 효과
- 여러개의 객체를 생성할 경우 모아서 한번에 쿼리를 전송한다.
- 영속성 상태의 객체가 생성 및 수정이 여러번 일어나더라도 해당 트랜잭션 종료시 쿼리는 1번만 전송될 수 있다.
- 영속성 상태에서 객체가 생성되었다 삭제되었다면 실제 DB에는 아무 동작이 전송되지 않을 수 있다.
- 즉, 여러가지 동작이 많이 발생하더라도 쿼리는 트랜잭션당 최적화 되어 최소쿼리만 날라가게된다.
- 키 생성전략이 generationType.IDENTITY 로 설정 되어있는 경우 생성쿼리는 쓰기지연이 발생하지 못한다.
- why? 단일 쿼리로 수행함으로써 외부 트랜잭션에 의한 중복키 생성을 방지하여 단일키를 보장한다.
'Back-End > JPA' 카테고리의 다른 글
| JPA 연관관계 Mapping (0) | 2024.12.10 |
|---|---|
| JPA - Entity 만들기 (0) | 2024.12.10 |
| JPA의 지연 저장소, 변경 감지, 그리고 flush와 Commit (1) | 2024.12.10 |
| JPA - 영속성 컨텍스트(Entity) (0) | 2024.12.10 |
| JPA에 대해서 (2) | 2024.12.10 |