엔티티를 조회한 순간의 필드 값들을 그대로 복사해 둔 배열. 하이버네이트 내부에서는
EntityEntry.loadedState 라는 이름의 Object[] 로 들고 있다.
왜 필요한가
JPA는 setter가 호출됐다는 사실을 알 방법이 없다. 자바에는 "이 필드가 바뀌었다"를
알려 주는 훅이 없기 때문이다. 그래서 처음 값을 따로 적어 두고 나중에 대조하는
방식을 쓴다. 이 적어 둔 값이 스냅샷이다.
메모리에 어떻게 놓이는가
Member(id=1, name="김", age=20) 을 조회하면 힙에 두 덩어리가 생긴다.
다이어그램 로딩 중…
- 엔티티 인스턴스 — 우리가
setter로 만지는 실제 객체 - 스냅샷 배열 — 조회 시점 값의 사본. 엔티티와 별개의 메모리에 있다
m.setName("이") 를 하면 엔티티 인스턴스의 name만 "이"로 바뀌고,
스냅샷 배열은 여전히 [1, "김", 20] 이다. 이 차이가 변경의 증거가 된다.
언제 만들어지고 언제 사라지나
| 시점 | 스냅샷 |
|---|---|
find()·JPQL로 조회 | 생성 |
persist() 로 새로 저장 | 생성(INSERT 이후 값 기준) |
| flush | 현재 값과 비교 후, 보낸 값으로 갱신 |
트랜잭션 종료·detach() | 폐기 |
비용 — 그래서 readOnly가 의미 있다
엔티티 하나당 필드 수만큼 배열이 하나 더 생긴다. 1만 건을 조회하면 엔티티 1만 개 + 스냅샷 배열 1만 개가 힙에 올라간다.
조회만 하는 트랜잭션에 @Transactional(readOnly = true) 를 붙이면 하이버네이트가
flush 모드를 MANUAL로 두고 스냅샷을 만들지 않는다. 메모리와 비교 비용이 함께 사라진다.
대량 조회에서 체감이 크다.