영속성 컨텍스트와 엔티티 생명주기, 1차 캐시 + 쓰기 지연 + 더티 체킹
흐름을 파악해보자~

Entity: DB에 매핑될 객체 (영속 객체)EntityManagerEntityManagerFactory: Entity Manager를 생성EntityTransactionQuery: 조건에 맞는 엔티티 검색Persistence: EntityManagerFactory를 생성Persistence Unit: DB 연결을 위한 설정 단위
persist())find()로 조회한 엔티티도 영속 상태이다.detach())clear(), close() 시에도 준영속 상태가 된다.트랜잭션이 끝나면 엔티티 매니저도 닫히면서, 영속성 컨텍스트가 종료되고 엔티티는 자동으로 준영속 상태가 된다
대량의 데이터를 영속 상태로 유지하면 메모리 과부하가 발생할 수 있다.
그래서 일정 단위마다 flush() → clear()를 호출하여 준영속화 시킨다.
remove())Map<식별자, 엔티티>구조로 관리된다.자동 생성해줌entitymanager.getTransaction().begin();
// [1] 비영속 상태 (Persistence Context에 없음)
Member member = new Member("감자");
// [2] 영속 상태 등록
// - 1차 캐시에 등록됨
// - 쓰기 지연 저장소에 INSERT 쿼리 준비
entitymanager.persist(member);
// [3] 1차 캐시에서 조회 (DB 접근 없음)
entitymanager.find(Member.class, member.getId());
// [4] 값 변경 → 더티 체킹 대상
member.setName("고구마");
// [5] 트랜잭션 커밋
// - 더티 체크 → UPDATE 쿼리 생성
// - flush 실행 → INSERT & UPDATE 쿼리 실제로 DB에 전송
entitymanager.getTransaction().commit();
entitymanager.getTransaction().begin();
Member member = new Member("감자");
System.out.println("===== persist 전 =====");
entitymanager.persist(member);
System.out.println("===== persist 후 =====");
System.out.println("===== 변경 전 =====");
member.setName("고구마");
System.out.println("===== 변경 후 =====");
System.out.println("===== 커밋 전 =====");
entitymanager.getTransaction().commit();
System.out.println("===== 커밋 후 =====");

위 테스트는 엔티티 id 전략을 SEQUENCE로 했을 경우이다.
동일 코드를 IDENTITY로 설정하여 다시 돌려보았다.

IDENTITY 전략을 사용하면, JPA는 ID 값을 DB에서 받아와야 하니까 persist() 순간에 바로 insert 쿼리를 날린다.
처음에 IDENTITY로 먼저 테스트를 진행했는데 INSERT 쿼리가 영속 시점에 날라가서 당황했다..
IDENTITY vs SEQUENCE의 INSERT 시점 차이| 전략 | ID 생성 방식 | INSERT 실행 시점 | 차이 이유 |
|---|---|---|---|
| IDENTITY | DB가 ID 생성 (ex: AUTO_INCREMENT) | persist() 호출 시 즉시 INSERT | ID 값을 DB가 정하므로, 미리 insert해서 ID 받아와야 함 |
| SEQUENCE | 시퀀스에서 미리 ID 조회 | commit() 또는 flush() 시 INSERT | Hibernate가 미리 ID를 확보하므로 INSERT는 나중에 가능 (쓰기 지연 효과 O) |