JPA OrphanRemoval 사용 시 복합 키 엔터티 삭제 버그

xlwdn·2022년 11월 29일
0

문제 원인


@OneToMany(mappedBy = "recruitmentBusiness", orphanRemoval = true, cascade = [CascadeType.REMOVE])
    var needCertificateList: MutableSet<CertificateUsage> = HashSet()
        protected set
@Entity
@Table(name = "ceritifcate_usage")
@IdClass(CertificateUsageIdClass::class)
class CertificateUsage(
    certificate: Certificate,
    recruitmentBusiness: RecruitmentBusiness
):BaseTimeEntity(), Persistable<String>, Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "certificate_id")
    val certificate: Certificate = certificate

    @Id
    @ManyToOne
    @JoinColumn(name = "recruitment_business_id")
    var recruitmentBusiness: RecruitmentBusiness? = recruitmentBusiness
        protected set
fun removeNeedCertificate(certificateName: String) {
        this.needCertificateList.filter {
            it.certificate.name == certificateName
        }.map {
            this.needCertificateList.remove(it)
        }
    }

기존, 위와 같이 모집공고 Entity에서 Certificate 삭제를 관리하던 중, Certificate이 삭제되지 않는 버그를 발견했습니다.

문제 원인


JPA에선 변경된 자식을 먼저 insert 하고 삭제시킨 자식은 상위 객체에 대한 fk값을 NULL로 update 합니다. 그리고 orphanRemoval 옵션을 true 로 하면 기존 NULL처리된 자식(= 고아 객체)을 DELETE 합니다.

일반적인 Entity였다면 orphanRemoval 옵션에 의해 정상적으로 상위 Entity에서 삭제 시 하위 엔터티 자체가 삭제되어야했으나 CertificateUsgae Entity는 m:m 관계를 관리하기 위한 테이블로써, 복합키 구성을 가지고 있었기에 recruitmentBusiness 필드를 null 값으로 구성한다하여도 pk값이 null로 취급되지 않았고, 때문에 작동하지 않은것으로 보입니다.

문제 해결


@Entity
@Table(name = "ceritifcate_usage")
class CertificateUsage(
    certificate: Certificate,
    recruitmentBusiness: RecruitmentBusiness
):BaseTimeEntity(), Persistable<String>, Serializable {

		@Id
		val id: String = UUID.randomUUID().toString()

    @ManyToOne
    @JoinColumn(name = "certificate_id")
    val certificate: Certificate = certificate

    @ManyToOne
    @JoinColumn(name = "recruitment_business_id")
    val recruitmentBusiness: RecruitmentBusiness = recruitmentBusiness

위와 같이 하위 엔터티의 PK값을 복합키에서 UUID로 변경하여 고아객체로 판단할 수 있도록 하였습니다.

0개의 댓글