1. 영속성 컨텍스트란? 🏠
영속성 컨텍스트는 엔티티를 영구 저장하는 환경을 의미
눈에 보이지 않는 가상의 데이터베이스 같은 공간으로, 엔티티 객체를 보관하고 관리
EntityManager em = emf.createEntityManager(); // 영속성 컨텍스트 생성
em.persist(entity); // 엔티티를 영속성 컨텍스트에 저장
2. 엔티티의 생명주기 (4가지 상태) 🔄
2-1. 비영속(new/transient) 상태 ⚪
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 그냥 자바 객체를 생성한 상태 (new 키워드로 생성)
Member member = new Member(); // 순수한 객체 상태 (비영속)
member.setId("member1");
member.setUsername("회원1");
2-2. 영속(managed) 상태 🟢
- 영속성 컨텍스트에 관리되는 상태
- persist() 메서드를 통해 영속성 컨텍스트에 저장된 상태
- 또는 DB에서 조회한 엔티티도 영속 상태
em.persist(member); // 객체를 영속성 컨텍스트에 저장 (영속)
Member findMember = em.find(Member.class, "member1"); // 조회한 엔티티도 영속 상태
2-3. 준영속(detached) 상태 🟡
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 영속성 컨텍스트가 제공하는 기능을 사용할 수 없음
em.detach(member); // 특정 엔티티만 준영속 상태로 전환
em.clear(); // 영속성 컨텍스트를 완전히 초기화
em.close(); // 영속성 컨텍스트를 종료
2-4. 삭제(removed) 상태 🔴
- 삭제된 상태로, 실제 DB 삭제를 요청한 상태
em.remove(member); // 객체를 삭제한 상태 (삭제)
3. 영속성 컨텍스트의 특징과 이점 ✨
3-1. 1차 캐시 📦
- 영속 상태의 엔티티는 모두 이곳에 저장됨
- Map<@Id, 엔티티> 형태로 저장
- 조회 시 DB보다 먼저 1차 캐시에서 찾음 (성능 향상)
// 1차 캐시에 저장됨
em.persist(member);
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
// 1차 캐시에 없으면 DB에서 조회 후 1차 캐시에 저장하고 반환
Member findMember2 = em.find(Member.class, "member2");
3-2. 동일성(identity) 보장 🔄
- 같은 트랜잭션 안에서는 같은 엔티티를 반환 (== 비교 true)
- 1차 캐시 덕분에 가능한 기능
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // true (동일성 보장)
3-3. 트랜잭션을 지원하는 쓰기 지연 ⏱️
- 트랜잭션 커밋할 때까지 SQL을 모았다가 한번에 전송
- 버퍼링 기능으로 성능 최적화 가능
EntityTransaction tx = em.getTransaction();
tx.begin(); // 트랜잭션 시작
em.persist(memberA); // INSERT SQL 생성 → 쓰기 지연 SQL 저장소에 저장
em.persist(memberB); // INSERT SQL 생성 → 쓰기 지연 SQL 저장소에 저장
// 여기까지 SQL을 DB에 보내지 않음
tx.commit(); // 커밋하는 순간 DB에 SQL 모아서 보냄
3-4. 변경 감지(Dirty Checking) 🔍
- 엔티티의 변경사항을 자동으로 감지
- 트랜잭션 커밋 시점에 스냅샷과 비교해서 변경된 엔티티 찾음
- 변경된 엔티티가 있으면 UPDATE SQL 생성 및 실행
Member member = em.find(Member.class, "member1");
member.setUsername("변경된 이름"); // 엔티티 수정
// em.update(member) 같은 코드가 필요 없음!
tx.commit(); // 변경 감지 → UPDATE SQL 실행
3-5. 지연 로딩(Lazy Loading) 🐢
- 연관된 엔티티를 실제 사용하는 시점에 로딩
- 성능 최적화에 유용
Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // 프록시 객체
String teamName = team.getName(); // 실제 team을 사용하는 시점에 DB에서 조회
4. 준영속 상태의 특징 ⚠️
4-1. 준영속 상태로 만드는 방법
- detach(entity): 특정 엔티티만 준영속 상태로 전환
- clear(): 영속성 컨텍스트를 완전히 초기화
- close(): 영속성 컨텍스트를 종료
4-2. 준영속 상태의 특징
- 영속성 컨텍스트가 제공하는 기능을 사용할 수 없음
- 식별자 값은 가지고 있음
- 지연 로딩 불가능
- 변경 감지 기능 동작 안 함
Member member = em.find(Member.class, "member1"); // 영속 상태
em.detach(member); // 준영속 상태로 전환
member.setUsername("변경"); // 변경해도 DB에 반영되지 않음
5. 병합(merge) - 준영속 → 영속 🔄
5-1. 병합이란?
- 준영속 상태의 엔티티를 다시 영속 상태로 변경하는 기능
- 준영속 엔티티의 식별자 값으로 영속 엔티티 조회
- 없으면 새로 생성해서 병합 (save or update 기능)
// 준영속 상태의 엔티티
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 준영속 엔티티를 영속 상태로 변경
Member mergedMember = em.merge(member);
5-2. 병합 동작 방식
- merge() 실행
- 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시 조회
- 없으면 DB에서 조회하고, 그래도 없으면 새로운 엔티티 생성
- 준영속 엔티티의 값을 영속 엔티티에 모두 복사
- 영속 상태의 엔티티를 반환
'Back-End' 카테고리의 다른 글
| 동기와 비동기 (1) | 2025.05.30 |
|---|---|
| [Redis] 기본 개념 (1) | 2025.05.26 |
| [DDD] 도메인 주도 설계 애그리거트 (0) | 2025.05.22 |
| [Spring] Mybatis (1) | 2025.05.08 |
| [Spring] 어노테이션 세부사항 정리 (1) | 2025.04.24 |