영속성 컨텍스트


영속성 컨텍스트

- 엔티티를 영구적으로 저장하는 환경
- 앱과 DB사이의 객체를 보관하는 가상의 DB역할
- **엔티티 메니저**를 통해 저장하거나 조회한다.


영속성 컨텍스트의 특징

  • 엔티티 매니저를 생성할 때 하나 만들어진다.
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하고 관리할 수 있다.

persist() 와 merge()

구분 persist() merge()
용도 새로운 엔티티를 영속성 컨텍스트에 저장 영속 상태의 엔티티 반환(영속상태와 무관)
상태 비영속 상태 → 영속 상태 식별자(id)로 찾을 수 있으면 병합하여 조회, 없으면 새로 생성하여 병합
반환 값 void 영속 상태의 새로운 엔티티
중복 처리 중복 ID가 있으면 예외 발생 중복 ID가 있으면 UPDATE 처리
영속성 컨텍스트 새로운 엔티티로 등록 이미 존재하는 엔티티를 병합
JPA의 save() 함수는?

save()함수는 내부적으로 isNew(Entity)연산을 수행하여,
해당 엔티티가 신규로 등록되는 엔티티이면 persist()를, 기존에 존재하는 엔티티면 merge()를 수행한다.

+JPA의 FlushMode.AUTO(기본)에서는 “현재 컨텍스트에 반영된 변경 사항을쿼리 실행 전에 DB에 동기화(flush) 한다.

1 - HfMarketingCode A = new HfMarketingCode();  
2 - A.setHitCode("testCode1");  
3 - A.setCodeName("testName2");  
  
4 - hfMarketingCodeRepository.save(A);  
  
5 - HfMarketingCode B = hfMarketingCodeRepository.findAllByHitCode("testCode1");
  • testCode1을 id로 갖는 testName1의 값을 testName2로 바꿀때, 4번이 아닌, 5번 시점에 update가 발생한다.

엔티티 생명주기


1. 비영속(new/transient) 상태: 영속성 컨텍스트와 관계가 없는 상태

// 객체를 생성한 상태 (비영속)
Member member = new Member();
// 객체의 값 변경해도 영속상태에 영행 X
member.setName("이태성");

2. 영속(managed) 상태: 영속성 컨텍스트에 저장된 상태

// 객체를 생성한 상태(비영속)
Member member = new Member();
member.setName("이태성");

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//엔티티 매니저를 사용하여 영속상태로 등록
em.persist(member);

3. 준영속(detached) 상태: 영속성 컨텍스트에 저장되었다가 분리된 상태

// 엔티티 매니저를 사용하여 영속상태를 분리 -> 준영속 상태
em.detach(member);
  • 메모리에 식별자 값을 갖고는 있지만, 영속성 컨텍스트의 어떠한 동작도 하지 않느 상태

4. 삭제(removed) 상태: 삭제된 상태

// 엔티티 매니저를 사용하여 영속상태를 상태 -> 삭제 상태
em.remove(member);

// 영속성 컨텍스트를 비워도 관리되던 엔티티는 준영속성 상태가 된다.
em.clear();

// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태가 된다.
em.close();
  • 영속성 컨텍스트를 비우거나(Clear) 종료해도(Close) 엔티티는 삭제되지 않는다(→ 준영속상태가 된다)
  • 오직 삭제(Remove)를 통해서만 영속상태를 삭제상태로 변경할 수 있다.


영속성 컨텍스트의 장점

1. 1차 캐시

  • 영속 상태의 엔티티를 1차 캐쉬에 저장하여, 같은 엔티티를 조회할시 DB에서 재조회 하지 않아도 된다.

2. 영속 엔티티의 동일성 보장

Member a = em.find(Member.class, "A");
Member b = em.find(Member.class, "A");


System.out.println(a==b) // true
  • 1차 캐시로 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공해 줄 수 있다.

3. 트랜잭션을 지원하는 쓰기 지연

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

transaction.begin(); // 트렌젝션 시작

em.persist(memberA);
em.persist(memberB);

// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 트렌젝션 커밋

영속성 컨텍스트에서 관리하는 객체는 수정되어도 DB에 바로 Insert 쿼리를 날리지 않는다. SQL 쿼리들을 모아놓았다가 flush(커밋) 될 때 모아둔 쿼리를 모두 날린다.

4. 변경 감지(Dirty Checking)

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();

// 영속 엔티티 조회
Member member = em.find(Member.class, "A");

// 영속 엔티티 데이터 수정
member.setName("이태성");
member.setJob("프로그래머");

transaction.commit(); // [트랜잭션] 커밋
  • 엔티티매니저에서 엔티티를 find()후 해당 객체의 값을 수정 후 커밋하면 어떻게 될까?
  • 따로 save나 update문이 없어 변경이 적용되지 않을것 같지만, 자동으로 변경점을 감지하여 반영된다.
  • 변경감지는 지연쓰기또한 동작한다.

플러시

  • flush()는 영속성 컨텍스트의 변경 내용을 DB에 반영하는 행위이다.

플러시의 흐름

  1. 변경 감지가 동작해서 스냅샷(트렌젝션의 시작 시점)과 비교해서 변경점을 찾는다.
  2. 변경된 엔티티에 대해서 수정 쿼리를 만들고 SQL 저장소에 등록한다.
  3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 반영한다.
  4. 트랜잭션 커밋시 자동 호출
  5. JPQL 쿼리 실행 시 자동 호출
em.flush()