[JPA] A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
12 Jul 2022이슈 사항
발생 예외
Caused by: org.springframework.orm.jpa.JpaSystemException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: kr.co.api.product.domain.classification.ProductClassification.metaHistories; nested exception is org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
productRepository.save(products)
와 같이 새로운 정보를 insert 하려 할 시 위와 같은 예외처리가 발생
소스
@Setter
@OneToMany(mappedBy = "productOrder", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products;
...
if (CollectionUtils.isEmpty(this.products)) {
products = new ArrayList<>(tempProducts);
} else {
products.addAll(tempProducts);
}
발생 원인
해당 소스에서 위와 같은 예외가 발생한 이유는 기존 자식 컬렉션인 products
를 초기화 하여 새로운 데이터를 넣어주기 위해 new ArrayList<>()
를 통해 기존의 인스턴스를 날리고 새로운 참조를 부여하는 과정에서 발생한 이슈입니다.
기존의 products
에서 새로운 참조를 부여함에 따라 자바 컬렉션의 관점에서는 초기화가 되었습니다. 하지만 JPA의 관점에서는 살짝 다르게 작용합니다.
JPA에서는 products
의 컬렉션과 해당 Entity와 연결이 해제되었지만. 세션(영속성 컨텍스트)에는 남아있게 되어 해당 예외가 발생하게 됩니다.
해당 이슈는 아래와 같이 수정할 수 있습니다.
수정된 소스
@Setter
@OneToMany(mappedBy = "productOrder", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products = new ArrayList<>();
...
if (CollectionUtils.isEmpty(this.products)) {
products.clear();
}
products.addAll(tempProducts);
위의 소스에서 new ArrayList<>()
와 같이 새로운 인스턴스를 생성 하는것이 아닌, products.clear()
를 통해서 기존 부모 Entity와 연결을 해제하지 않고 컬렉션을 비우고 새로운 값을 넣어주었다.
이렇게 하면 세션(영속성 컨텍스트)의 연결이 틀어지지않고 기존의 연결을 유지하여 데이터를 추가 및 변경하도록 수정할 수 있게 된다.