Close
Close full mode
logo만렙 개발자 키우기

(6) 준영속

Git RepositoryEdit on Github
Last update: 9 months ago by nowwaterReading time: 2 min

영속 -> 준영속

영속성 컨텍스트가 관리하는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 것을 준영속 상태라고 한다.

따라서 준영속 상태의 엔티티는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.

준영속 상태를 만드는 방법 3가지

  1. em.detach(entity) : 특정 엔티티만 준영속 상태로 전환

  2. em.clear() : 영속성 컨텍스트를 완전히 초기화

  3. em.close() : 영속성 컨텍스트를 종료

3.6.1 엔티티를 준영속 상태로 전환 : detach()

: 특정 엔티티 하나를 준영속 상태로 만드는 메소드

public void testDetached() {
...
// 회원 엔티티 생성, 비영속 상태
Member member = new Member();
member.setId("memberA");
member.setUsername("회원A");
// 회원 엔티티 영속 상태
em.persist(member);
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
transaction.commit(); //트랜잭션 커밋
}
  • em.detach(member)
  • 해당 엔티티를 관리하지 말라고 하는 것
  • 메소드를 호출하는 순간 1차 캐시부터 쓰기 지연 SQL저장소까지 해당 엔티티를 관리하기 위한 모든 정보가 제거된다.

image

image

요약

  • 영속 상태 : 영속성 컨텍스트로부터 관리되는 상태
  • 준영속 상태 : 영속성 컨텍스트로부터 분리된 상태

3.6.1 영속성 컨텍스트를 초기화 : clear()

: 영속성 컨텍스트를 초기화해서 해당 영속성 컨텍스트의 모든 엔티티를 준영속 상태로 만든다.

//엔티티 조회, 영속 상태
Member member = em.find(Member.class, "memberA");
em.clear(); //영속성 컨텍스트 초기화
//준영속 상태
member.setUsername("changeName");

영속성 컨텍스트 초기화 전

image

영속성 컨텍스트 초기화 후

image

memberA 와 memberB 는 준영속 상태가 된다. -> 변경감지 동작 X


3.6.3 영속성 컨텍스트 종료 : close()

영속성 컨텍스트를 종료하면 해당 영속성 컨텍스트가 관리하던 영속 상태의 엔티티가 모두 준영속 상태가 된다.

public void closeEntityManager() {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("jpabook");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] - 시작
Member memberA = em.find(Member.class, "memberA");
Member memberB = em.find(Member.class, "memberB");
transaction.commit(); // [트랜잭션] = 커밋
em.close(); // 영속성 컨텍스트 닫기(종료)
}

영속성 컨텍스트 제거 전

image

영속성 컨텍스트 제거 후

image

영속성 컨텍스트에서 더 이상 memberA, memberB 를 관리하지 않는다. -> 준영속 상태


3.6.4 준영속 상태의 특징

거의 비영속 상태에 가깝다

1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떤 기능도 동작하지 않는다.

식별자 값을 가지고 있다.

준영속 상태는 이미 한 번 영속 상태였으므로 반드시 식별자 값을 가지고 있다.

지연 로딩을 할 수 없다.

지연 로딩 시 문제가 발생한다. -> 영속성 컨텍스트가 더는 관리하지 않기 때문

지연 로딩 : 실제 객체 대신 프록시 객체를 로딩해두고 해당 객체를 실제로 사용할 때 영속성 컨텍스트를 통해 데이터를 불러오는 방법


3.6.5 병합 : merge()

  • 준영속 상태의 엔티티를 다시 영속 상태로 변경하려면 병합을 사용하면 된다.
  • merge() 메소드는 준영속 상태의 엔티티를 받아서 그 정보로 새로운 영속 상태의 엔티티를 반환한다.
// merge() 메소드 정의
public <T> T merge(T entity);
// 사용 예
Member mergeMember = em.merge(member);

준영속 병합

image

1. merge()를 실행한다.

2. 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.

3. 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고 1차 캐시에 저장한다.

4. 조회한 영속 엔티티(mergeMember)member의 값을 채워 넣는다.

member 엔티티의 모든 값을 mergeMember에 밀어 넣는다.
이때, mergeMember의 "회원1" 이라는 이름이 "회원명 변경"으로 바뀐다.
-> member.setName("회원명 변경") 을 실행한 상태

5. mergeMember를 반환한다.

member 는 여전히 준영속 상태지만, mergeMember가 영속 상태인 채로 남아있다. -> 서로 다른 인스턴스

따라서 준영속 상태인 member는 이제 사용할 필요가 없으므로 영속 엔티티를 참조하도록 변경하는 것이 안전하다.

// Member mergeMember = em.merge(member); // 아래 코드로 변경
member = em.merge(member);

비영속 병합

병합은 비영속 엔티티도 영속 상태로 만들 수 있다.

Member member = new Member();
Member newMember = em.merge(member); //비영속 병합
tx.commit();
  1. 파라미터로 넘어온 엔티티의 식별자 값으로 영속성 컨텍스트를 조회한다.

  2. 엔티티가 없으면 데이터베이스에서 조회한다.

  3. 데이터베이스에서도 발견하지 못하면 새로운 엔티티를 생성해서 병합한다.

  • 병합은 준영속, 비영속을 신경쓰지 않는다.
  • 식별자 값으로 엔티티를 조회할 수 있으면 불러서 병합, 조회할 수 없으면 새로 생성해서 병합
  • 병합은 save or update 기능을 수행한다.
Previous
(5) 플러시
Next
(7) 트랜잭션 범위의 영속성 컨텍스트