코드 그라데이션

고아 객체, 그리고 생명 주기 본문

Spring/JPA 공부

고아 객체, 그리고 생명 주기

완벽한 장면 2023. 8. 25. 16:00

고아 객체

 

예제

Parent 수정

  @OneToMany(mappedBy = "parent",cascade = CascadeType.ALL, orphanRemoval = true) 
  // 양방향 매핑 완료, CASCADE옵션 추가, 고아객체 제거
  private List<Child> childList = new ArrayList<>();

JpaMain

public class JpaMain {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();

    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try {
      Child child1 = new Child();
      Child child2 = new Child();

      Parent parent = new Parent();
      parent.addChild(child1);
      parent.addChild(child2);

      em.persist(parent);

	 // 추가한 부분 
      em.flush();
      em.clear();

      Parent findParent = em.find(Parent.class, parent.getId());
      findParent.getChildList().remove(0); // 첫 번째 것 지움
      
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
      e.printStackTrace(); // 하나 찍어봄
    } finally {
      em.close();
    }
    emf.close();
  }
}

 

실행하면

14:24:42.779 [main] DEBUG org.hibernate.SQL - 

Hibernate: 
    select
        childlist0_.parent_id as parent_i3_2_0_,
        childlist0_.id as id1_2_0_,
        childlist0_.id as id1_2_1_,
        childlist0_.name as name2_2_1_,
        childlist0_.parent_id as parent_i3_2_1_ 
    from
        Child childlist0_ 
    where
        childlist0_.parent_id=?
14:24:42.780 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl - Preparing collection initializer : [inflearn.exjpa.jpaExample.Parent.childList#1]
14:24:42.781 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl - Found row of collection: [inflearn.exjpa.jpaExample.Parent.childList#1]
14:24:42.781 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl - Found row of collection: [inflearn.exjpa.jpaExample.Parent.childList#1]
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving attributes for [inflearn.exjpa.jpaExample.Child#2]
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Processing attribute `name` : value = null
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Attribute (`name`)  - enhanced for lazy-loading? - false
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Processing attribute `parent` : value = 1
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Attribute (`parent`)  - enhanced for lazy-loading? - false
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [inflearn.exjpa.jpaExample.Child#2]
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving attributes for [inflearn.exjpa.jpaExample.Child#3]
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Processing attribute `name` : value = null
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Attribute (`name`)  - enhanced for lazy-loading? - false
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Processing attribute `parent` : value = 1
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Attribute (`parent`)  - enhanced for lazy-loading? - false
14:24:42.781 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [inflearn.exjpa.jpaExample.Child#3]
14:24:42.781 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections were found in result set for role: inflearn.exjpa.jpaExample.Parent.childList
14:24:42.781 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - Collection fully initialized: [inflearn.exjpa.jpaExample.Parent.childList#1]
14:24:42.782 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections initialized for role: inflearn.exjpa.jpaExample.Parent.childList
14:24:42.782 [main] DEBUG org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer - Done loading collection
14:24:42.782 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl - committing
14:24:42.782 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Processing flush-time cascades
14:24:42.783 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Dirty checking collections
14:24:42.783 [main] DEBUG org.hibernate.engine.spi.CollectionEntry - Collection dirty: [inflearn.exjpa.jpaExample.Parent.childList#1]
14:24:42.783 [main] DEBUG org.hibernate.engine.internal.Collections - Collection found: [inflearn.exjpa.jpaExample.Parent.childList#1], was: [inflearn.exjpa.jpaExample.Parent.childList#1] (initialized)
14:24:42.783 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 1 deletions to 3 objects
14:24:42.783 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 0 (re)creations, 1 updates, 0 removals to 1 collections
14:24:42.783 [main] DEBUG org.hibernate.internal.util.EntityPrinter - Listing entities:
14:24:42.783 [main] DEBUG org.hibernate.internal.util.EntityPrinter - inflearn.exjpa.jpaExample.Child{parent=inflearn.exjpa.jpaExample.Parent#1, name=null, id=2}
14:24:42.783 [main] DEBUG org.hibernate.internal.util.EntityPrinter - inflearn.exjpa.jpaExample.Parent{name=null, childList=[inflearn.exjpa.jpaExample.Child#3], id=1}
14:24:42.783 [main] DEBUG org.hibernate.internal.util.EntityPrinter - inflearn.exjpa.jpaExample.Child{parent=inflearn.exjpa.jpaExample.Parent#1, name=null, id=3}
14:24:42.784 [main] DEBUG org.hibernate.SQL - 

Hibernate: 
    /* delete inflearn.exjpa.jpaExample.Child */ delete 
        from
            Child 
        where
            id=?

DELETE 쿼리가 한 번 날아간 걸 볼 수가 있고,

 

H2 가서 확인하면

하나가 사라져 있음을 확인할 수 있음(ID : 2)

 

orphanRemoval 을 넣어두면

이 컬렉션에서 빠진 아이는 자동으로 삭제가 되는 옵션이다.

 


고아 객체 주의

 

마지막 내용 설명(CascadeType.REMOVE 처럼 동작)

Parent 수정

@OneToMany(mappedBy = "parent", orphanRemoval = true) // CASCADE옵션 제거함
private List<Child> childList = new ArrayList<>();

 

JPAMain 수정

public class JpaMain {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();

    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try {
      Child child1 = new Child();
      Child child2 = new Child();

      Parent parent = new Parent();
      parent.addChild(child1);
      parent.addChild(child2);

      em.persist(parent);
      em.persist(child1);
      em.persist(child2);

      em.flush();
      em.clear();

      Parent findParent = em.find(Parent.class, parent.getId());

      em.remove(findParent); // 조회한 parent를 아예 지워버림
      
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
      e.printStackTrace(); // 하나 찍어봄
    } finally {
      em.close();
    }
    emf.close();
  }
}

// => 그럼 orphanRemoval 입장에서는 이 컬렉션은 다 날아간 것임. 그래서 자식까지 다 delete 된다.

 

실행 결과

14:37:25.849 [main] DEBUG org.hibernate.SQL - 

Hibernate: 
    /* delete inflearn.exjpa.jpaExample.Child */ delete 
        from
            Child 
        where
            id=?

Hibernate: 
    /* delete inflearn.exjpa.jpaExample.Child */ delete 
        from
            Child 
        where
            id=?

Hibernate: 
    /* delete inflearn.exjpa.jpaExample.Parent */ delete 
        from
            Parent 
        where
            id=?

쿼리 3번 나갔음을 확인할 수 있음.

 


영속성 전이 + 고아 객체, 그리고 생명 주기

 

3번 내용 예제

public class JpaMain {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();

    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try {
      Child child1 = new Child();
      Child child2 = new Child();

      Parent parent = new Parent();
      parent.addChild(child1);
      parent.addChild(child2);

      em.persist(parent);

      em.flush();
      em.clear();

      Parent findParent = em.find(Parent.class, parent.getId());
      findParent.getChildList().remove(0);
      
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
      e.printStackTrace(); // 하나 찍어봄
    } finally {
      em.close();
    }
    emf.close();
  }
}

 

Parent 수정

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL ,orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

 

em.persist() 할 때도 parent만 했다.

그리고

em.remove() 할 때도 findParent만 했다.

자식을 persist 하지 않아도 자식이 저장된다.

자식을 remove 하지 않아도 자식이 지워진다.

 

중요한 것은 Parent는 JPA를 통해서 생명주기를 관리 중이다.

Child는 생명주기를 Parent가 관리하고 있다는 것.

 

728x90

'Spring > JPA 공부' 카테고리의 다른 글

기본 값 타입  (0) 2023.08.26
실전 예제 - 5. 연관관계 관리  (0) 2023.08.25
영속성 전이(CASCADE)  (0) 2023.08.25
지연 로딩 활용  (0) 2023.08.24
즉시 로딩  (0) 2023.08.24
Comments