티스토리 뷰
반응형
오늘은 간단하게 JPA에서 연관관계에 있는 엔티티를 입력받으려 할 떄 에러가 난 증상을 간단히 기술해보고자 합니다.
케이스는 아래와 같습니다.
A class
@Entity
@Data
...
public class Aclass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String col1;
private String col2;
private String col3;
private LocalDateTime createdAt;
}
B class
@Entity
@Data
...
public class Bclass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Aclass aclass;
private String col1;
private String col2;
private String col3;
private LocalDateTime createdAt;
}
즉 B class 는 A class 에 1:1 연관관계에 놓여있고, b 라는 테이블에는 A를 참조한 추가 데이터가 들어있는 구조입니다. A class에는 있을 수 있지만 B class에서 필요 없는 내용이 있는 경우입니다.
이렇게 한 후 bclassRepository.save(bclass) 를 하면 아래와 같은 에러가 발생합니다.
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.project.domain.Aclass; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.project.domain.Aclass
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:319)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy232.saveAll(Unknown Source)
가만히 생각해보니 연관관계가 다른 것 같습니다. cascade = CascadeType.ALL 에는 A class 에 있는 모든 내용을 적용한다는 내용인데 지금과 같은 상황에서는 사용하면 안될 것 같습니다.
영속성 전이인 cascade 옵션의 설명은 찾아보니 다음과 같습니다.
CascadeType의 종류
- CascadeType.RESIST: 엔티티를 생성하고, 연관 엔티티를 추가하였을 때 persist() 를 수행하면 연관 엔티티도 함께 persist()가 수행된다. 만약 연관 엔티티가 DB에 등록된 키값을 가지고 있다면 detached entity passed to persist Exception이 발생한다.
- CascadeType.MERGE: 트랜잭션이 종료되고 detach 상태에서 연관 엔티티를 추가하거나 변경된 이후에 부모 엔티티가 merge()를 수행하게 되면 변경사항이 적용된다.(연관 엔티티의 추가 및 수정 모두 반영됨)
- CascadeType.REMOVE: 삭제 시 연관된 엔티티도 같이 삭제됨
- CascadeType.DETACH: 부모 엔티티가 detach()를 수행하게 되면, 연관된 엔티티도 detach() 상태가 되어 변경사항이 반영되지 않는다.
- CascadeType.ALL: 모든 Cascade 적용
JPA Life Cycle은 특이하게 Cascade.ALL 옵션을 준 경우 save를 수행하면 자식 객체에서 참조하고 있는 부모객체가 detach 되는 구조로 라이프 사이클이 이루어지고 있습니다. 그래서 CascadeType.MERGED 를 적용하면 A class 내용이 변질될 가능성이 있어 위험요소가 있습니다.
그래서 저는 CascadeType.DETACH 옵션을 사용하여 적용하였습니다.
또한 CascadeType은 @OneToOne 뿐만 아닌 @ManyToOne 관계에서도 적용되니 참고해서 개발하면 좋을 것 같습니다.
반응형
'Server' 카테고리의 다른 글
Spring Batch 실행 명령어 (0) | 2022.03.13 |
---|---|
Ktor 프레임워크 공부하기 (1) | 2022.03.12 |
지금 봐도 괜찮은 배달의민족 도메인 설계 (1) | 2022.03.06 |
[Java] Rest Template 을 사용하여 API 통신하기 (1) | 2022.02.07 |
[Java] Encode, Decode 함수 사용하기 (4) | 2022.02.04 |
특정 잡만 수행하고 싶을 때 (1) | 2022.01.10 |
댓글
공지사항