[Spring] 영속성 컨텍스트 (2)

이연우·2025년 7월 28일

TIL

목록 보기
68/100

📦 1차 캐시

  • JPA에서 Entity가 영속되면 영속성 컨텍스트 내부의 1차 캐시에 저장되고,
    이후 같은 Entity를 조회하면 DB 조회 없이 캐시에서 반환

✅ 특징

  • 트랜잭션 단위로 작동
  • 같은 트랜잭션 내에서는 동일한 엔티티는 DB를 조회하지 않음

🗄️ 영속
→ 영속성 컨텍스트의 1차 캐시에 저장됨

// 비영속
Tutor tutor = new Tutor(1L, "wonuk", 100);

// 영속, 1차 캐시에 저장
em.persist(tutor);

🗄️ 영속된 Entity 조회
→ Database가 아닌 1차 캐시에 저장된 Entity를 먼저 조회

// 1차 캐시에서 조회
Tutor findTutor = em.find(Tutor.class, 1L);

🗄️ 데이터베이스에 저장된 데이터 조회
→ 1차 캐시는 동일한 트랜잭션 안에서만 사용이 가능
→ 요청이 들어오고 트랜잭션이 종료되면 영속성 컨텍스트는 삭제

📌 예시 코드 요약

Tutor tutor = new Tutor(1L, "wonuk", 100); // 비영속
em.persist(tutor);                         // 영속 → 1차 캐시에 저장
Tutor findTutor = em.find(Tutor.class, 1L); // DB 접근 없이 캐시 조회

🔍 실행 결과

  • persist 후 출력 → INSERT SQL은 실행되지 않음
  • find() 시 DB 접근 없이 캐시에서 조회됨
  • 트랜잭션 commit()INSERT SQL 실제 실행됨

♻️ 동일성 보장 (Identity Guarantee)

  • 같은 트랜잭션 내에서 같은 엔티티를 여러 번 조회하면 동일한 객체 인스턴스가 반환됨

→ 동일한 트랜잭션 내에서 조회된 Entity는 같은 인스턴스를 반환
→ DB에 저장된 데이터를 조회하여 1차 캐시에 저장
→ 1차 캐시에 저장된 데이터 조회

🎯 목적

  • 객체 비교 시 ==(주소 비교)true가 나옴
  • 마치 Java의 Map<Long, Entity> 처럼 작동

📌 예시 코드 요약

Tutor find1 = em.find(Tutor.class, 1L); // DB 조회 → 1차 캐시 저장
Tutor find2 = em.find(Tutor.class, 1L); // 1차 캐시에서 조회
System.out.println(find1 == find2);    // true

🔍 실행 결과

  • SQL 쿼리는 1회만 실행
  • 1차 캐시에 저장된 인스턴스를 재사용

✍️ 쓰기 지연 (Write-Behind)

  • JPA는 persist() 시 DB에 즉시 반영하지 않고
    쓰기 지연 저장소에 보관했다가, 트랜잭션 commit() 시점에 일괄 실행

⛓️ 동작 순서

  1. em.persist(tutor1) → 1차 캐시 저장 + INSERT SQL 쓰기 지연 저장소에 저장

  2. em.persist(tutor2) → 위와 동일

  3. transaction.commit() 시점에 INSERT SQL 일괄 실행

📌 예시 코드 요약

Tutor tutor1 = new Tutor(1L, "wonuk1", 100);
Tutor tutor2 = new Tutor(2L, "wonuk2", 200);

em.persist(tutor1);
em.persist(tutor2);

// 아직 DB에는 반영되지 않음
transaction.commit(); // 여기서 실제 INSERT SQL 실행됨

🔐 핵심 개념

용어설명
1차 캐시Entity 저장 및 조회용 메모리 캐시
쓰기 지연 저장소SQL 실행을 잠시 보관
flushSQL을 DB에 즉시 반영
commitflush가 자동으로 발생하고 트랜잭션 종료됨

⚙️ 고급 설정: hibernate.jdbc.batch_size

  • 여러 INSERT한 번에 묶어 JDBC BATCH 전송 가능
  • SQL 전송 횟수 줄이기 → 네트워크 비용 절감 + 성능 향상
spring.jpa.properties.hibernate.jdbc.batch_size=10

⚠️ 예외 상황

💡 @GeneratedValue(strategy = GenerationType.IDENTITY) 사용 시

→ ID 값을 DB에서 직접 생성하므로 persist() 즉시 SQL 실행
→ 이때는 쓰기 지연이 적용되지 않음


🧠 요약 정리

항목설명장점
📦 1차 캐시같은 Entity는 DB 접근 없이 캐시 조회성능 ↑
🔁 동일성 보장트랜잭션 내 동일 객체 보장 (== true)일관성 ↑
🕐 쓰기 지연SQL을 모아 한 번에 실행통신 비용 ↓
🧠 캐시 + 지연 저장소비슷하지만 목적 다름 (조회 vs 저장)책임 분리

0개의 댓글