Memo memo = new Memo(); // 비영속 상태
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("비영속과 영속 상태");
em.persist(memo);
@Test
@DisplayName("비영속과 영속 상태")
void test1() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = new Memo(); // 비영속 상태
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("비영속과 영속 상태");
em.persist(memo);
et.commit();
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}
- 코드
- 코드 디버깅 결과
- 비영속 상태이기 때문에 entitiesByKey=null
- 비영속 상태는 JPA가 관리하지 못하기 때문에 해당 객체의 데이터를 변경해도 변경 감지가 이루어지지 않음
- 코드
- 코드 디버깅 결과
- em.persist(memo); 메서드 호출 후 영속성 컨텍스트에 저장되었고 MANAGED 상태 즉, JPA가 관리하는 영속 상태의 Entity가 됨
영속 상태에서 준영속 상태로 바꾸는 방법
1. detach(entity)
em.detach(memo);
- detach(entity): 특정 Entity만 준영속 상태로 전환
- 영속성 컨텍스트에서 관리되다(Managed)가 분리된 상태(Detached)로 전환됨
@Test @DisplayName("준영속 상태 : detach()") void test2() { EntityTransaction et = em.getTransaction(); et.begin(); try { Memo memo = em.find(Memo.class, 1); 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(); }
- 따라서 memo Entity 객체의 데이터를 수정해도 변경감지 기능을 사용할 수 없어 Update SQL이 수행되지 않음
- em.contains(memo); 는 해당 객체가 영속성 컨텍스트에 저장되어 관리되는 상태인지 확인하는 메서드
- em.detach(memo); 이후 확인했을 때 false가 출력된 것을 확인할 수 있음
2.clear()
em.clear();
- clear() : 영속성 컨텍스트를 완전히 초기화
- 영속성 컨텍스트의 모든 Entity를 준영속 상태로 전환
- 영속성 컨텍스트 틀은 유지하지만 내용ㅇ은 비워 새로 만든 것과 같은 상태가 됨
- 따라서 계속해서 영속성 컨텍스트 이용 가능
@Test @DisplayName("준영속 상태 : clear()") void test3() { EntityTransaction et = em.getTransaction(); et.begin(); try { Memo memo1 = em.find(Memo.class, 1); Memo memo2 = em.find(Memo.class, 2); // em.contains(entity) : Entity 객체가 현재 영속성 >>컨텍스트에 저장되어 관리되는 상태인지 확인하는 메서드 System.out.println("em.contains(memo1) = " + >>em.contains(memo1)); System.out.println("em.contains(memo2) = " + >>em.contains(memo2)); System.out.println("clear() 호출"); em.clear(); System.out.println("em.contains(memo1) = " + >>em.contains(memo1)); System.out.println("em.contains(memo2) = " + >>em.contains(memo2)); System.out.println("memo#1 Entity 다시 조회"); Memo memo = em.find(Memo.class, 1); System.out.println("em.contains(memo) = " + >>em.contains(memo)); System.out.println("\n 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(); }
- em.clear(); 메서드 호출 후 em.contains(memo1,2); 확인했을 때 false가 출력되는 것을 확인 가능
- 다시 memo#1 Entity를 조회한 후 em.contains(memo); 확인했을 때 true가 출력된 것을 확인 가능
3. close()
em.close();
- close() : 영속성 컨텍스트를 종료
- 해당 영속성 컨텍스트가 관리하던 영속성 상태의 Entity들은 모두 준영속 상태로 변경됨
- 영속성 컨텍스트가 종료되었기 때문에 영속성 컨텍스트를 사용할 수 없음
@Test @DisplayName("준영속 상태 : close()") void test4() { EntityTransaction et = em.getTransaction(); et.begin(); try { Memo memo1 = em.find(Memo.class, 1); Memo memo2 = em.find(Memo.class, 2); // em.contains(entity) : Entity 객체가 현재 영속성 >>컨텍스트에 저장되어 관리되는 상태인지 확인하는 메서드 System.out.println("em.contains(memo1) = " + >>em.contains(memo1)); System.out.println("em.contains(memo2) = " + >>em.contains(memo2)); System.out.println("close() 호출"); em.close(); Memo memo = em.find(Memo.class, 2); // >>Session/EntityManager is closed 메시지와 함께 오류 발생 System.out.println("memo.getId() = " + >>memo.getId()); } catch (Exception ex) { ex.printStackTrace(); et.rollback(); } finally { em.close(); } emf.close(); }
- em.close(); : 메서드 호출 이후 EntityManager를 사용하려고 하자 오류가 발생
- 영속성 컨텍스트가 종료되면 계속해서 영속성 컨텍스트를 사용할 수 없다는 것 확인 가능
준영속 상태에서 다시 영속 상태로 바꾸는 방법
merge(entity)
em.merge(memo);
- merge(entity) : 전달받은 Entity를 사용하여 새로운 영속 상태의 Entity를 반환
- merge(entity) 동작
- 파라미터로 전달된 Entity의 식별자 값으로 영속성 컨텍스트 조회
- 해당 Entity가 영속성 컨텍스트에 없다면?
a. DB에서 새롭게 조회
b. 조회한 Entity를 영속성 컨텍스트에 저장
c. 전달 받은 Entity의 값을 사용하여 병합
d. Update SQL이 수행(수정)- 만약 DB에서도 없다면?
a. 새롭게 생성한 Entity를 영속성 컨텍스트에 저장
b. Insert SQL이 수행(저장)- 따라서 merge(entity) 메서드는 비영속, 준영속 모두 파라미터로 받을 수 있으며 상황에 따라 '저장'을 할 수도 '수정'을 할 수도 있음
1. merge(entity) 저장
@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(); }
- em.merge(memo); 호출 후 영속성 컨텍스트에 Memo#3 객체가 저장되고 Insert SQL이 추가된 것을 확인 가능
- em.merge(memo); 호출 후 영속성 컨텍스트에 해당 값이 없어 DB에 조회 했는데도 해당 값이 없기 때문에 새롭게 생성하여 영속성 컨텍스트에 저장하고 Insert SQL이 수행됨
- 비영속 상태의 memo는 merge()호출 후에 해당 memo 객체가 영속성 컨텍스트에 저장된게 아니라 새롭게 생성되어 영속성 컨텍스트에 저장되었기 때문에 false가 반환됨
- 새롭게 저장된 영속 상태의 객체를 반환받은 mergedMemo는 true가 반환됨
2. merge(entity) 수정
@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(); }
- detach() 메서드 호출로 조회해온 영속 상태의 memo 객체를 준영속 상태로 전환
- 준영속 상태의 memo의 값을 수정한 후 memo 객체를 사용해서 merge() 메서드를 호출
- memo 객체는 준영속 상태이기 때문에 현재 영속성 컨텍스트에는 해당 객체가 존재하지 않음
- 따라서 DB에서 식별자 값을 사용하여 조회한 후 영속성 컨텍스트에 저장하고 파라미터로 받아온 준영속 상태의 memo 객체의 값을 새롭게 저장한 영속 상태의 객체에 병합하고 반환
- 그 결과 반환된 mergedMemo의 content를 출력하였을 때 변경되었던 내용인 "merge() 수정"이 출력됨
- 트랜잭션 commit 후 Update SQL이 수행됨
- 준영속 상태의 Entity memo는 merge() 호출 후에도 영속성 컨텍스트에 저장되어 있지 않기 때문에 false가 반환됨
- 새롭게 저장된 영속 상태의 객체를 반환받은 mergedMemosms ture가 반환됨
em.remove(memo);
공감하며 읽었습니다. 좋은 글 감사드립니다.