Memo memo = new Memo(); // 비영속 상태
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("비영속과 영속 상태");detach(entity) : 특정 Entity만 준영속 상태로 전환합니다.
@Test
@DisplayName("준영속 상태 : detach()")
void test2() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = em.find(Memo.class, 1); // 여기는 MANAGED(영속)상태
System.out.println("memo.getId() = " + memo.getId());
System.out.println("memo.getUsername() = " + memo.getUsername());
System.out.println("memo.getContents() = " + memo.getContents());
// em.contains(entity) : Entity 객체가 현재 영속성 컨텍스트에 저장되어 관리되는 상태인지 확인하는 메서드
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("detach() 호출");
em.detach(memo); // 준영속 상태
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("memo Entity 객체 수정 시도");
memo.setUsername("Update");
memo.setContents("memo Entity Update");
System.out.println("트랜잭션 commit 전");
et.commit();
System.out.println("트랜잭션 commit 후");
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}
Hibernate:
select
m1_0.id,
m1_0.contents,
m1_0.username
from
memo m1_0
where
m1_0.id=?
memo.getId() = 1
memo.getUsername() = Robbert
memo.getContents() = @Transactional 전파 테스트 중! 2
em.contains(memo) = true
detach() 호출
em.contains(memo) = false
memo Entity 객체 수정 시도
트랜잭션 commit 전
트랜잭션 commit 후
속성 컨텍스트에서 제거되면서 준영속 상태로 전환되면 1차 캐시 즉, 캐시 저장소에서 제거되기 때문에 JPA의 관리를 받지 못해 영속성 컨텍스트의 어떠한 기능도 사용할 수 없습니다.
따라서 detached상태 일때 memo Entity 객체의 데이터를 수정해도 변경감지(Dirty Checking) 기능을 사용할 수 없어 Update SQL이 수행되지 않았습니다.
em.contains(memo); : 해당 객체가 영속성 컨텍스트에 저장되어 관리되는 상태인지 확인하는 메서드로 em.detach(memo); 이후 확인했을 때 false가 출력된 것을 확인할 수 있습니다.
em.close(); 메서드 호출 이후 EntityManager를 사용하려고 하면 오류가 발생합니다.close후 영속성 컨텍스트를 사용했을 시 에러
java.lang.IllegalStateException: Session/EntityManager is closed
at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:409)
at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:164)
at org.hibernate.internal.SessionImpl.find(SessionImpl.java:2313)
at org.hibernate.internal.SessionImpl.find(SessionImpl.java:2298)
at EntityStateTest.test4(EntityStateTest.java:148)
@Test
@DisplayName("merge() : 저장")
void test5() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = new Memo();
memo.setId(3L);
memo.setUsername("merge()");
memo.setContents("merge() 저장");
System.out.println("merge() 호출");
Memo mergedMemo = em.merge(memo);
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("em.contains(mergedMemo) = " + em.contains(mergedMemo));
System.out.println("트랜잭션 commit 전");
et.commit();
System.out.println("트랜잭션 commit 후");
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}
merge() 호출
Hibernate:
select
m1_0.id,
m1_0.contents,
m1_0.username
from
memo m1_0
where
m1_0.id=?
em.contains(memo) = false
em.contains(mergedMemo) = true
트랜잭션 commit 전
Hibernate:
/* insert com.sparta.entity.Memo
*/ insert
into
memo (contents, username, id)
values
(?, ?, ?)
트랜잭션 commit 후
em.merge(memo); 호출 후 영속성 컨텍스트에 해당 값이 없어 DB에 조회 했는데도 해당 값이 없기 때문에 새롭게 생성하여 영속성 컨텍스트에 저장하고 Insert SQL이 수행되었습니다.@Test
@DisplayName("merge() : 수정")
void test6() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = em.find(Memo.class, 3);
System.out.println("memo.getId() = " + memo.getId());
System.out.println("memo.getUsername() = " + memo.getUsername());
System.out.println("memo.getContents() = " + memo.getContents());
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("detach() 호출");
em.detach(memo); // 준영속 상태로 전환
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("준영속 memo 값 수정");
memo.setContents("merge() 수정");
System.out.println("\n merge() 호출");
Memo mergedMemo = em.merge(memo);
System.out.println("mergedMemo.getContents() = " + mergedMemo.getContents());
System.out.println("em.contains(memo) = " + em.contains(memo));
System.out.println("em.contains(mergedMemo) = " + em.contains(mergedMemo));
System.out.println("트랜잭션 commit 전");
et.commit();
System.out.println("트랜잭션 commit 후");
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}
Hibernate:
select
m1_0.id,
m1_0.contents,
m1_0.username
from
memo m1_0
where
m1_0.id=?
memo.getId() = 3
memo.getUsername() = merge()
memo.getContents() = merge() 저장
em.contains(memo) = true
detach() 호출
em.contains(memo) = false
준영속 memo 값 수정
merge() 호출
Hibernate:
select
m1_0.id,
m1_0.contents,
m1_0.username
from
memo m1_0
where
m1_0.id=?
mergedMemo.getContents() = merge() 수정
em.contains(memo) = false
em.contains(mergedMemo) = true
트랜잭션 commit 전
Hibernate:
/* update
com.sparta.entity.Memo */ update memo
set
contents=?,
username=?
where
id=?
트랜잭션 commit 후
비영속 > 영속 > 준영속 | 삭제)① 비영속(new/transient) - 엔티티 객체가 만들어져서 아직 저장되지 않은 상태로, 영속성컨텍스트와 전혀 관계가 없는 상태
② 영속(managed) - 엔티티가 영속성 컨텍스트에 저장되어, 영속성 컨텍스트가 관리할 수 있는 상태
③ 준영속(detached) - 엔티티가 영속성 컨텍스트에 저장되어 있다가 분리된 상태로, 영속성 컨텍스트가 더 이상 관리하지 않는 상태
④ 삭제(removed) - 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제하겠다고 표시한 상태
객체의 영속성 상태는 Entity Manager 의 메소드를 통해 전환된다.
(비영속상태) -> persist(),merge() ->(영속성 컨텍스트에 저장된 상태) -> flush() -> (DB에 쿼리가 전송된 상태)(DB에 쿼리가 반영된 상태)