Book/스프링부트 핵심가이드

영속성 전이와 고아 객체

블로그 주인장 2023. 11. 15.

영속성 전이와 고아 객체

특정 엔티티의 영속성을 변경하는 '영속성 전이' 와 연관관계가 끊어진 '고아 객체' 에 대해 알아보겠습니다.


 

영속성 전이


특정 엔티티의 영속성 상태 변경 시에 연관된 엔티티의 상태가 변경되는 것을 의미한다.

 

[예시 : @OneToMany]

public @interface OneToMany {
    Class targetEntity() default void.class;

    CascadeType[] cascade() default {};

    FetchType fetch() default FetchType.LAZY;

    String mappedBy() default "";

    boolean orphanRemoval() default false;
}
  • casade() : 해당 어노테이션은 영속성 전이를 설정하는 데에 활용된다.

 

영속성 전이 타입의 종류

종류 설명
ALL 모든 영속 상태 변경에 대해 영속성 전이를 적용
PERSIST 엔티티가 영속화할 때 연관된 에티티도 함께 영속화
MERGE 엔티티를 영속성 컨텍스트에 병합할 때 연관된 엔티티도 병합
REMOVE 엔티티를 제거할 때 연관된 엔티티도 제거
REFRESH 엔티티를 새로고침할 때 연관된 엔티티도 새로고침
DETACH 엔티티를 영속성 컨텍스트에서 제외하면 연관된 엔티티도 제외
  • 영속성 전이에 사용되는 타입은 엔티티 생명주기와 연관되어 있다.
  • casade( ) 요소의 값으로 영속 상태의 변경이 일어나면 매핑으로 연관된 엔티티에도 동일한 동작이 일어나도록 전이하는 것이다.
  • casade( ) 요소의 리턴 타입은 배열 형식이다.  

 

영속성 전이 적용


[예시] 상품 엔티티와 공급업체 엔티티

한 가게가 새로운 공급업체와 계약하며 새로운 상품을 입고시키는 상황에 영속성 전이가 적용되는 것을 확인

 

공급업체 엔티티에 영속성 전이 설정

public class Provider extends BaseEntity {
	//.. 중략

    @OneToMany(mappedBy = "provider", cascade = CascadeType.PERSIST)
    @ToString.Exclude
    private List<Product> productList = new ArrayList<>();
}

 

영속성 전이 테스트

@Test
void cascadeTest() {
    Provider provider = savedProvider("새로운 공급 업체");

    Product product1 = savedProduct("펜", 1000, 100);
    Product product2 = savedProduct("가방", 10000, 1000);
    Product product3 = savedProduct("가위", 5000, 500);

    //연관관계 설정
    product1.setProvider(provider);
    product2.setProvider(provider);
    product3.setProvider(provider);

    provider.getProductList().addAll(Lists.newArrayList(product1, product2, product3));

    providerRepository.save(provider);
}
  • 공급업체 1개와 상품 객체를 3개 생성한다.
  • 영속성 전이 테스트를 위해 객체에는 영속화 작업을 하지 않고, 연관관계(setProvider) 만 설정한다.
  • "providerRepository.save(provider)" 부분은 영속성 전이가 수행되는 부분이다.
  • 영속성 전이를 사용하면 부모엔티티가 되는 Provider 엔티티만 저장하면 코드에 작성되어 있는 Cascade.PERSIST 에 맞춰서 상품 엔티티도 함께 저장이 가능하다.

 

고아 객체


  • JPA에서 고아(orpahn)는 부모 엔티티와 연관관계가 끊어진 엔티티를 의미한다. 
  • JPA에는 이러한 고아 객체를 자동으로 제거하는 기능이 있다.
  • 자식 엔티티가 다른 엔티티와 연관관계를 가지고 있다면 해당 기능은 사용하지 않는 것이 좋다.

 

공급업체 엔티티에 고아 객체 제거 기능 추가

public class Provider extends BaseEntity {

    //..중략
    
    @OneToMany(mappedBy = "provider", cascade = CascadeType.PERSIST, orphanRemoval = true)
    @ToString.Exclude
    private List<Product> productList = new ArrayList<>();
}
  • 'orphanRemoval = true' 속성은 고아 객체를 제거하는 기능이다.

 

고아 객체 제거 기능 테스트

  • 테스트 연관 매핑 수행 : 공급 업체 엔티티 1개, 상품 엔티티 3개
  • 첫 번째 매핑 상품 엔티티 삭제 : 연관관계가 끊긴 상품의 엔티티가 제거된다.
@Test
void orphanRemovalTest() {
    Provider provider = savedProvider("새로운 공급 업체");

    Product product1 = savedProduct("펜", 1000, 100);
    Product product2 = savedProduct("가방", 10000, 1000);
    Product product3 = savedProduct("가위", 5000, 500);

    product1.setProvider(provider);
    product2.setProvider(provider);
    product3.setProvider(provider);

    provider.getProductList().addAll(Lists.newArrayList(product1, product2, product3));

    providerRepository.saveAndFlush(provider);

    providerRepository.findAll().forEach(System.out::println);
    productRepository.findAll().forEach(System.out::println);

    Provider foundProvider = providerRepository.findById(1L).get();
    foundProvider.getProductList().remove(0);   //첫번째 인덱스 임의 삭제

    providerRepository.findAll().forEach(System.out::println);
    productRepository.findAll().forEach(System.out::println);
}
반응형

댓글