LUMI_dev

Entity- #5. Entity의 상태 본문

스파르타 코딩 클럽 | 자바 심화 과정/Spring Master (입문 주차)

Entity- #5. Entity의 상태

luminous_dev 2025. 2. 3. 23:57

 

1. 비영속 (Transient) 
2. 영속 (Managed)
3. 준영속 (Detached)
4. 삭제 (Removed)

 

 


1. 비영속 (Transient) 

  • new 연산자를 통해 인스턴스화된 Entity 객체
  • 아직 영속성 컨텍스트에 저장 안됨 → JPA의 관리 받지 않음 
  • JPA가 관리하지 못하기 때문에 해당 객체의 데이터를 변경해도 변경 감지 이뤄지지 않음 

 

Memo memo = new Memo(); // 비영속 상태
memo.setId(1L);
memo.setUsername("Robbie");
memo.setContents("비영속과 영속 상태");

비영속 상태이기 때문에 entitiesByKey=null


2. 영속 (Managed)

.persist(entity) : EntityManager를 통해 비영속 Entity를 영속성 컨텍스트에 저장 / 관리되고 있는 상태됨  

em.persist(memo);

영속성 컨텍스트에 저장되었고 MANAGED 상태 즉, JPA가 관리하는 영속 상태의 Entity가 됨

 


3. 준영속 (Detached)

영속성 컨텍스트에 저장되어 관리되다가 분리된 상태

 

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

detach(entity) 특정 Entity만 준영속 상태로 전환
Managed가 Detached로 전환
clear() 영속성 컨텍스트 완전히 초기화
close() 영속성 컨텍스트 종료

 

방법 1. detach(entity)

em.detach(memo);

 

코드 

더보기
@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();
}

 

 

 

 

 

 

em.find(Memo.class,1); 메서드 호출 후 Managed 상태

 

 

 

 

 

 

 

 

 

 

 

 

em.detach(memo); 메서드를 호출하여 

특정 Entity 객체 Memo #1을 영속성 컨텍스트에서 제거

→ 준영속 상태로 전환 

 

 

 

 

 

 

 

 

memoEntity 객체의 데이터를 수정해도 변경 감지 x

→ Update SQL 수행되지 않음

 

 


 

em.contains(memo);

객체가 영속성 컨텍스트에 저장되어 관리되는 상태인지

확인하는 메서드 

 


 

 

 

 

 

 

 

 

방법 2. clear(); 

영속성 컨텍트 초기화

em.clear();
  • 영속성 컨텍스트의 모든 Entity를 준영속 상태로 전환
  • 영속성 컨텍스트 틀은 유지하지만 내용은 비워 새로 만든 것과 같은 상태 
  • 영속성 컨텍스트 계속 이용할 순 있음  

코드 

더보기
@Test
@DisplayName("준영속 상태 : clear()")
void test3() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

		//memo#1 Entity를 조회
        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));

		//clear()시키기 - .contains() = false
        System.out.println("clear() 호출");
        em.clear();
        System.out.println("em.contains(memo1) = " + em.contains(memo1));
        System.out.println("em.contains(memo2) = " + em.contains(memo2));

		//memo#1 Entity를 다시 조회 - .contains() = true
        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.clear(); 메서드 호출 후 em.contains(); 메서드 확인했을 때 false

 

 

 

 

 

 

 

 

 

 

 

 

 

 

방법 3. close(); 

영속성 컨텍스트를 종료

em.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(); 메서드 호출 후 EntityManger 사용하려고 하면 오류 발생

영속성 컨텍스트가 종료되었기 때문


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

merge(entity)

em.merge(memo);

 

  • 전달 받은 Entity를 사용하여 새로운 영속 상태의 Entity 반환
  • 비영속, 준영속 모두 파라메터로 받을 수 있음
  • 상황에 따라 '저장' or '수정' 함
  • 동작 방법
    • 파라메터로 전달된 Entity 식별자 값으로 영속성 컨텍스트 조회
      • 해당 Entity가 영속성 컨텍스트에 없다면?
        • DB에서 새롭게 조회
        • 조회한 Entity, 영속성 컨텍스트에 저장
        • 전달 받은 Entity 값을 사용하여 merge(병합)
        • Update SQL이 수행 (수정) 
      • 만약 DB에도 없다면? 
        • 새롭게 생성한 Entity를 영속성 컨텍스트에 저장
        • Insert SQL 수행 (저장)

예시)

A. 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 = false

merged된 memo = true

 

 

 

 

 

 

 

 

B. 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();
}

 

 

 

 

 

 

 

영속성 컨텍스트에는 없으나 DB에 존재할 경우

1. DB에서 식별자 값을 사용하여 조회한 후

2. 영속성 컨텍스트에 저장 > 객체에 병합 및 반환

3. 트랜잭션 commit 후 Update SQL 수행

 

 

 

 

 


4. 삭제 (Removed)

삭제하기 위해 조회해온 영속 상태의 Entity를 파라메터로 전달 받아 삭제 상태로 전환 

em.remove(memo);