🔸 EntityManager
- 매 요청마다 (=스레드 하나 생성마다) Entity Manager 생성 (by EntityManagerFactory)
- 이 EntityManager은 내부적으로 DB 커넥션 풀을 이용해 DB에 접근함
📌 영속성 컨텍스트
- 엔티티를 영구 저장하는 환경
EntityManager.persist(entity);
- 원래 persist(object) → DB에 object data 저장하는 것
- But 실제로는 DB 저장X, 영속성 컨텍스트에 저장 → DB 저장 순서를 거친다
- 언제 DB 저장? Transactional 해당 스레드가 끝나고 commit일 때!
- 영속성 컨텍스트에 저장 = 엔티티를 영속화시킴
- 영속성 컨텍스트는 논리적 개념 ∴ EntityManager로 영속성 컨텍스트에 접근함
🔸 엔티티 생명주기
🔸 영속 (managed) / 비영속 (new, transient)
(↔비영속 : 객체를 생성하기만 한 상태(new))
- 영속성 컨텍스트에 저장된 상태 → Entity가 영속성 컨텍스트에 의해 관리됨
- DB에 저장 X 고로 쿼리도 DB로 안날아감
- 트랜잭션의 Commit 시점에 DB로 쿼리 날아감
✅ .persist(entity)
- 트랜잭션 내부에서 persist() 발생
→ 엔티티들을 1차 캐시에 저장 + 쓰기 지연 SQL 저장소에 해당 엔티티를 DB에 저장하는 insert 쿼리 생성해 쌓아둠
(쿼리 날리지 않고 기다림)
- commit() 하는 시점에서! 쌓아놓은 쿼리들을 DB에 몽땅 날린 후 (= flush()) 다시 commit()
즉, 트랜잭션 커밋하면 → flush(), commit() 두 가지 수행함
🔸 준영속 (detached)
- 엔티티가 영속성 컨텍스트에 저장 → 분리된 상태
🔸 삭제 (removed)
- 객체 삭제된 상태 → DB에서도 삭제
📌영속성 컨텍스트의 이점
✅ 1차 캐시
- 영속성 컨텍스트(EntityManager)에는 내부에 1차 캐시 존재
- managed 상태 되는 순간 → 1차 캐시에 { id : Entity자체} 로 저장
- 이렇게 되면 find() 하는 순간 → 1차 캐시 scan해서 먼저 찾음 → hit하면 DB까지 안감
- 만약 1차 캐시에 Data 없다면 → DB에서 꺼내 1차 캐시 저장 후 반환
- 이름처럼 기기구조에서 배운 캐시 작동원리와 매우 유사하다
- 1차 캐시는 각 스레드 (@Transactional 붙은 애들)마다 별도로 존재함. 공유X
- 100명한테 100개 요청오면 → EntityManager 100개 생성 + 1차 캐시 100개
✅ 동일성 보장
- 영속 엔티티의 Identity 보장 → 여러 번 조회(REPEATABLE READ)해도 같은 래퍼런스
📌더티 체킹 (Dirty Checking)
🔸더티 체킹 : 상태 변경 검사
- JPA: 트랜잭션 끝난 후 → (최초 조회 상태 기준)변화 있는 모든 엔티티 객체를 DB에 자동 갱신
- 최초 조회 상태 기준?
- 엔티티 조회 → 해당 엔티티 조회 당시의 상태 ➡️ 스냅샷에 기록
- 최초 조회 상태 기준?
🔸 즉, 엔티티 수정이 발생했을 경우 … (managed 상태인 엔티티 한정)
- 엔티티 데이터만 수정(set)하면 끝
- 트랜잭션을 commit하면 자동으로 update 쿼리 날아감
- 1차 캐시에 저장 + 스냅샷 필드도 함께 저장하기 때문
- commit/flush 발생 → 엔티티 - 스냅샷 비교
- 변경 사항 O : UPDATE SQL 만들어서 → 쓰기지연 SQL 저장소에 저장→ flush()로 DB로 반영 후 commit 함
🔸 변경 부분만 update 하기
- 기본값 : 기본적으로 모든 필드들을 update함 (JPA 기본 방식)
- 쿼리 재사용성이 뛰어나서…
- 하지만 필드가 많아질 경우 되려 부담이 될 수도 있음
➕ @DynamicUpdate
- 필드 선택적으로 업데이트 가능 → 변경 필드만 반영
- 엔티티 최상단에 선언 해주면 됨
'Spring' 카테고리의 다른 글
DI(Dependency Injection : 의존성 주입)에 대하여 (1) | 2024.01.09 |
---|