반응형
JPA에서 제공하는 조회(Select), 등록(Insert), 수정(Update), 삭제(Delete) 기능은
모두 영속성 컨텍스트와 밀접하게 연관되어 있습니다.
예제를 통해 각 기능의 실행 흐름과 콘솔 로그 결과를 확인해보겠습니다.
1. 조회 – 1차 캐시 동작 확인
EntityManager는 조회 시 먼저 1차 캐시를 확인하고,
이미 같은 트랜잭션 내에서 조회한 엔티티라면 DB 쿼리를 실행하지 않습니다.
try {
// 1차 캐시 테스트
Member findMember1 = em.find(Member.class, 101L); // 첫 조회 → DB SQL 실행
Member findMember2 = em.find(Member.class, 101L); // 두 번째 조회 → 1차 캐시에서 반환
// 동일 객체인지 비교 (1차 캐시 활용 여부 확인)
System.out.println("result = " + (findMember1 == findMember2));
System.out.println("===============================");
tx.commit();
} catch (Exception e) {
tx.rollback();
}
💻 실행 결과
Hibernate:
select member0_.id as id1_0_0_, member0_.username as username2_0_0_
from Member member0_ where member0_.id=?
result = true
===============================
📌 해설
- 첫 번째 em.find() → DB 쿼리 실행 후, 영속성 컨텍스트 1차 캐시에 저장됩니다.
- 두 번째 em.find() → DB를 조회하지 않고, 1차 캐시에서 동일 객체를 반환합니다.
- findMember1 == findMember2 → true
→ 동일한 엔티티 인스턴스라는 것을 의미합니다.
2. 등록 – 쓰기 지연 (Transactional Write-Behind)
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Member member1 = new Member(150L, "A");
Member member2 = new Member(160L, "B");
// 엔티티를 영속성 컨텍스트에 등록 (아직 DB에 INSERT SQL 전송 안 함)
em.persist(member1);
em.persist(member2);
System.out.println("===============================");
// 여기까지 INSERT SQL 실행 안 됨
transaction.commit(); // 커밋 시 SQL 실행
💻 실행 결과
===============================
Hibernate:
insert into Member (username, id) values (?, ?)
Hibernate:
insert into Member (username, id) values (?, ?)
📌 해설
- persist()를 호출하면 엔티티는 영속 상태로 전환되지만,
즉시 SQL이 실행되지 않고 쓰기 지연 저장소에 보관됩니다. - commit() 시점에 한꺼번에 INSERT SQL을 전송하여 DB I/O 성능을 최적화합니다.
3. 수정 – 변경 감지 (Dirty Checking)
JPA는 엔티티를 em.find()로 조회하여 영속 상태로 만든 뒤,
값을 변경하면 커밋 시점에 자동으로 UPDATE SQL을 실행합니다.
Member member = em.find(Member.class, 150L);
member.setName("ZZZZZ"); // 값 변경
System.out.println("===============================");
tx.commit(); // 변경 감지 후 UPDATE SQL 실행
💻 실행 결과
Hibernate:
select member0_.id as id1_0_0_, member0_.username as username2_0_0_
from Member member0_ where member0_.id=?
===============================
Hibernate:
update Member set username=? where id=?
📌 해설
- em.update() 같은 메서드가 없는 이유는, JPA가 변경 감지 기능을 지원하기 때문입니다.
- 커밋 시점에 엔티티 스냅샷과 현재 값을 비교하여 변경된 필드만 UPDATE 합니다.
4. 삭제 – 엔티티 제거
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA);
💻 실행 결과
Hibernate:
select member0_.id as id1_0_0_, member0_.username as username2_0_0_
from Member member0_ where member0_.id=?
Hibernate:
delete from Member where id=?
📌 해설
- 삭제 대상은 영속 상태여야 하므로, 먼저 find()로 조회해야 합니다.
- remove() 호출 시 즉시 DELETE SQL이 실행되는 것이 아니라,
커밋 시점에 실행됩니다.
📊 JPA 엔티티 상태 흐름 다이어그램
[비영속] (new/transient)
│
│ em.persist()
▼
[영속] (managed) ←────────────┐
│ ▲ │
│ │ em.find() or JPQL 조회 │
│ │ │
│ em.detach(), clear(), close()
▼
[준영속] (detached)
│
│ em.remove()
▼
[삭제] (removed)
📌 정리
- 조회: 1차 캐시 활용, 동일 PK 재조회 시 DB 접근 없음
- 등록: 쓰기 지연을 통해 커밋 시 INSERT SQL 실행
- 수정: 변경 감지로 UPDATE SQL 자동 실행
- 삭제: 조회 후 remove() 호출 시 DELETE SQL 실행
반응형
'Back end > Spring Project' 카테고리의 다른 글
| [Spring] JPA 준영속 상태(Detached) (0) | 2025.08.15 |
|---|---|
| [Spring] JPA flush (0) | 2025.08.14 |
| [Spring] Maven profile 이용한 환경별 (local, dev, prod) 빌드 및 설정 분리 (0) | 2025.07.23 |
| [Spring] BeanFactory와 ApplicationContext (0) | 2025.07.17 |
| [Spring] AnnotationConfigApplicationContext로 알아보는 스프링 컨테이너 생성 과정 (0) | 2025.07.15 |