
영속성 컨텍스트
엔티티 매니저 안에 위치하여
트랜잭션 내에서 DB를 조회한 엔티티를 보관하는 일종의 Map 형태의 캐시 저장소
영속성 컨텍스트는 Map 형태의 캐시 저장소를 갖고 있는데,
트랜잭션 내에서 엔티티가 DB를 조회했다면 이 캐시 저장소에 저장된다
이 저장소에 저장되는 것을 1차 캐시에 저장된다고 표현 한다
후에 1차 캐시를 확인하고 캐시에 동일한 ID를 지닌 엔티티 객체가 있으면
DB를 조회하지 않고 캐시의 엔티티를 즉시 반환한다
영속성 컨텍스트의 특징 - 동일성 보장
1차 캐시 안의 엔티티의 ID와 같은 트랜잭션 안에서 같은 ID인 엔티티는
항상 동일한 메모리 주소의 인스턴스 ( == true) → 동일성 보장
1차 캐시를 통해 같은 트랜잭션 내 엔티티와 동일하다면 반환하는 이 작업 덕분에
DB의 사용량을 줄일 수 있음 → 성능 향상
영속성 컨텍스트의 특징 - 더티 체킹
만약 1차 캐시 안 엔티티 객체가 트랜잭션 내 엔티티와 상태가 다르다면 어떻게 할까?
영속성 컨텍스트가 커밋 직전에 상태를 감지하는데, 다르다면
JPA 는 변경된 엔티티의 상태에 맞춰 Update 문을 DB 로 보내 업데이트를 실행한다
영속성 컨텍스트의 특징 - 쓰기 지연
save() 호출, 즉 INSERT 의 수행도 영속성 컨텍스트는 INSERT 를 받아
바로바로 진행하는 것이 아니라 트랜잭션이 커밋되는 순간에 한번에 진행한다
사실 업데이트와 딜리트도 마찬가지로 이렇게 모아서 진행한다
이렇게 모아서 SQL을 DB로 보내 진행하는 과정을 Flush (강제 동기화) 라고 한다
이 Flush 는 엔티티 매니저를 통해 명령어로 수행할 수 있는데,
이 때 커밋이 되는게 아니고 SQL만 보낸 상태 임을 유의해야 한다
툴에서 사용으로 치면 그냥 DDL 만 실행 시키고 커밋을 치지 않은 상태가 되는 것
수동 Flush 를 왜 하는 걸까?
우선 주 목적은 메모리 절약을 위해서 한다
1만 건 INSERT, UPDATE 문이 한번에 엔티티 매니저에 쌓이면…그래서, entityManager.flush() 를 통해 일정 쌓인 SQL문을 보내고
clear() 를 통해 영속성 컨텍스트를 비우는 작업을 수행해야 한다그 외에 꼭 수동으로 해야하는 경우들이 있는데
→ JPQL 에서 nativeQuery 사용시 DB에 직접 접근하여 SQL을 실행하므로 필요
→SQL의 실행 순서가 중요한 경우
(로직에서 아이디 같은 식별자가 먼저 insert 되어야 하는 경우)
→ 언급한 대량의 UPDATE / INSERT 구문들을 처리 할 때
엔티티의 생명주기
비영속 → 영속 → 준영속 → 삭제
비영속 : JPA 가 아직 모르는 객체 상태
영속 : 영속성 컨텍스트가 관리중인 상태 (find 같은 조회나 persist 되었을 때)
준영속 : 영속성 컨텍스트에서 분리된 상태
(트랜잭션이 종료되었거나 detach, clear, close 되었을 때)삭제 : 삭제 대기 중인 상태 (쓰기 지연으로 대기 중)
이래서 엔티티를 리턴하면 준영속 상태로 리턴되므로 DTO에 담아 사용하라는 것
준영속 상태를 영속 상태로 돌릴 수 있긴 하나..
merge를 사용하면 되지만 DB를 강제로 한번 조회해야하는 일이 있고
만약 준영속 상태의 엔티티 안에 변경사항이
그대로 반영되거나, 일부만 있는 경우엔 나머지가 NULL 로 덮이므로
사용하지 않아야 하는 메서드이다
EntityManager
JPA 의 핵심 인터페이스, 어플리케이션과 데이터베이스 사이에서
엔티티 객체를 관리하며 실제 데이터베이스 작업을 수행한다
Persistence Unit → EntityManagerFactory → EntityManager 의 단계
를 거쳐서 JPA 의 영속성 컨텍스트가 구성된다