TIL

엔티티 매니저가 무엇일까?

상태: JPA

영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경
  • 1차 캐싱, 쓰기 지연, 변경 감지를 통해서 영속 로직을 효율적으로 할 수 있게 돕는다
  • 영속 로직은 어떻게 수행될 수 있나? → 엔티티가 영속성 컨텍스트에 관리되어야 한다 → how? 엔티티 매니저

엔티티 매니저

  • 영속 로직을 수행하는 주체; 엔티티가 영속성 컨텍스트에 관리되는 것을 돕는다
  • 엔티티의 상태를 변경하고, 영속성 컨텍스트 ↔ 엔티티 상호작용

조금 더 구체적으로 엔티티 매니저의 역할을 설명해 주실 수 있을까요?

엔티티의 상태 종류: 비영속, 영속, 준영속, 삭제

현재 상태 실행 메서드 결과 상태 설명
비영속 (New) em.persist(entity) 영속 (Managed) 컨텍스트에 저장 및 관리 시작
준영속 (Detached) em.merge(entity) 영속 (Managed) 분리된 엔티티를 다시 관리 상태로 병합
영속 (Managed) em.detach(entity) 준영속 (Detached) 특정 엔티티만 관리 대상에서 제외
영속 (Managed) em.remove(entity) 삭제 (Removed) DB와 컨텍스트 모두에서 삭제 예약
관리 중 전체 em.clear() / close() 준영속 (Detached) 컨텍스트 통째로 비우기 / 종료

엔티티 매니저의 역할

  • 엔티티의 상태를 변경하는 주체 → 위의 함수들을 이용해 수행한다.
  • 쓰기 지연 저장소에 있는 쿼리를 flush해서 DB와 동기화
  • JPQL이나 Native Query를 이용해서 직접 DB로부터 데이터를 불러올 수도 있다.

엔티티의 각 상태에 대해서 설명해주세요.

1. 비영속 상태

Member member = new Member("산초");
  • 엔티티 객체가 새로 생성은 되었지만, 아직 영속성 컨텍스트에 올라가지 않은 상태
  • 메모리 상에만 존재해서 DB와 전혀 관련이 없다.

2. 영속 상태

em.persist(member);
em.merge(detagedMember);
em.find(Member.class, 1L);
  • 엔티티 객체가 영속성 컨텍스트에서 관리되고 있는 상태
  • 엔티티의 변경 사항이 자동으로 DB에 반영된다.

3. 준영속 상태

em.detach(member);
em.clear();
em.close();
  • 엔티티 객체가 한 번 영속성 컨텍스트에 의해 관리는 되었음. 그러나 지금은 영속성 컨텍스트와 분리된 상태 (DB에 저장은 되어있음)
  • 엔티티 객체의 변경 사항이 더 이상 데이터베이스에 반영되지 않음
  • 어떻게 준영속 상태가 되는가?
    • 영속성 컨텍스트 종료
    • 트랜잭션 종료

4. 삭제 상태

em.remove(member);
  • 엔티티 객체가 영속성 컨텍스트에서 제거된 상태
  • 엔티티 객체가 DB에서 삭제됨

변경 감지가 어떻게 수행될까?

변경 사항이 자동으로 반영된다.

이때 변경 사항을 스냅샷현재 엔티티 상태와 비교해서 update쿼리를 작성하게 된다.

  • update 쿼리는 쓰기 지연 저장소에 담는다.

1) 언제 스냅샷을 찍는가?

  • DB에서 조회할 때: em.find()나 JPQL을 통해 DB에서 데이터를 가져와 영속성 컨텍스트(1차 캐시)에 올리는 그 순간 찍습니다.
  • 새로 생성해서 관리할 때: em.persist(member)를 호출하여 비영속 객체를 영속 상태로 만들 때 그 초기 상태를 찍습니다.

2) 그럼 언제 update 쿼리가 DB로 날리게 될까?

  1. 비교 시점: 트랜잭션을 끝내려 할 때(커밋 시점)나 개발자가 명시적으로 flush()를 호출할 때 비로소 엔티티와 스냅샷을 비교합니다.
  2. 쿼리 생성: 비교 결과 변경된 내용이 있으면 그때서야 UPDATE SQL을 만들어 쓰기 지연 저장소에 쌓아둡니다.
  3. 전송: 쓰기 지연 저장소에 쌓인 쿼리들은 오직 flush가 일어날 때 한꺼번에 DB로 전송됩니다.

3) Flush와 Commit의 관계: flushcommit

  • 순서는 무조건 flush가 먼저 일어난다.

tx.commit()을 호출하면 JPA 내부에서는 다음과 같은 일이 벌어집니다.

  1. flush() 자동 호출:
    • 엔티티와 스냅샷을 비교해서 변경 감지(Dirty Checking).
    • 수정/삭제/삽입 쿼리를 생성해서 쓰기 지연 저장소에 담기.
    • 쓰기 지연 저장소의 모든 쿼리를 DB에 전송 (실제 SQL 실행).
  2. DB Transaction Commit:
    • DB가 전송받은 쿼리들을 최종적으로 확정(물리적 저장).

✅ 왜 이 순서인가요?

  • DB 입장에서는 SQL 쿼리를 먼저 받아야(Flush),
  • 그 내용을 기반으로 커밋을 할지 말지 결정할 수 있기 때문입니다.

비유하자면:

  • Flush: 쇼핑몰에서 결제하기 전 장바구니에 담긴 물건들을 계산대로 보내서 바코드를 찍는 과정 (실제 돈은 안 나감, 취소 가능).
  • Commit: “결제하기” 버튼을 눌러서 돈이 실제로 빠져나가고 거래를 확정 짓는 과정.

✅ 예외 포인트

  • 직접 em.flush()를 호출하지 않아도 tx.commit()만 하면 알아서 플러시가 수행되어 DB에 반영됩니다.
  • 보통 flush()를 직접 호출하는 경우커밋 전에 미리 쿼리를 날려서 DB 상태를 확인해야 할 때(예: JPQL 실행 전)” 정도뿐입니다.

💡 정리하자면

  • 스냅샷은 영속성 컨텍스트에 들어올 때 찍는다.
  • 쿼리 생성 및 전송flush 일어난다.
  • commit은 내부적으로 flush를 먼저 실행한 뒤 DB 트랜잭션을 끝낸다.

추가 학습 자료를 공유합니다.

results matching ""

    No results matching ""