Entity의 상태

금은체리·2023년 11월 14일
1

Spring

목록 보기
3/49

1. 비영속(Transient)

Memo memo = new Memo(); // 비영속 상태
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("비영속과 영속 상태");
  • new 연산자를 통해 인스턴스화 된 Entity 객체
  • JPA의 관리를 받지 않음
    • 아직 영속성 컨텍스트에 저장되지 않았기 때문
    • JPA: 자바 진영에서 ORM(Object-Relational Mapping)의 기술 표준으로 사용하는 인터페이스의 모음
      • ORM: 데이터베이스를 사용하려면 SQL 쿼리(query)라는 구조화된 질의를 작성하고 실행하는 등의 복잡한 과정이 필요하다. 이때 ORM(object relational mapping)을 이용하면 자바 문법만으로도 데이터베이스를 다룰 수 있다.

2. 영속(Managed)

em.persist(memo);
  • persist(entity): 비영속 Entity를 EntityManager를 통해 영속성 컨텍스트에 저장하여 관리되고 있는 상태로 만듦
  • 영속(Managed)상태일 때만 변경 감지 진행 = 수정이 자동으로 이루어짐
@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가 됨

3. 준영속(Detached)

  • 준영속 상태는 영속성 컨텍스트에 저장되어 관리되다가 분리된 상태를 의미
  • 영속성 컨텍스트가 제공하는 기능을 사용하지 못함
    • 변경감지가 안됨 ➡️ update문이 실행 안됨

영속 상태에서 준영속 상태로 바꾸는 방법

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의 식별자 값으로 영속성 컨텍스트 조회
      1. 해당 Entity가 영속성 컨텍스트에 없다면?
        a. DB에서 새롭게 조회
        b. 조회한 Entity를 영속성 컨텍스트에 저장
        c. 전달 받은 Entity의 값을 사용하여 병합
        d. Update SQL이 수행(수정)
      2. 만약 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가 반환됨

4. 삭제(Removed)

em.remove(memo);
  • remove(entity) : 삭제하기 위해 조회해온 영속 상태의 Entity를 파라미터로 전달받아 삭제 상태로 전환

참고

[점프 투 스프링부트]2-03 JPA

profile
전 체리 알러지가 있어요!

1개의 댓글

comment-user-thumbnail
2023년 11월 14일

공감하며 읽었습니다. 좋은 글 감사드립니다.

답글 달기