JPA Cascade & OrphanRemoval
Cascade는 엔티티간의 생명주기를 같이 다룰 수 있습니다.OrphanRemoval는 객체상의 변화가 DB 까지 적용됩니다.
Goal
Cascade와OrphanRemoval옵션에 대해서 알아봅니다.- 각 옵션을 사용할때 주의할점을 알아봅니다.
Entity Setting
Team(상위 / 부모) MyMeber(하위 / 자식)
자식엔티티
MyMember.kt
1 | |
부모 엔티티
Team.kt
1 | |
- Team(One) MyMember(Many) 양방향 매핑을 맺어주었습니다.
Cascade
Cascade 비활성화
Team 에 Member 를 넣어두고,
TeamReposity만을 이용해 저장하는 경우
1 | |
1 | |


- 양방향 매핑이여도 Team 이 주체이기 때문에, Cascade 옵션이 NONE 이면 Team 만 저장되고 Member 는 같이 DB에 저장되지 않습니다.
CascadeType.PERSIST
1 | |

- Team과 함께 Member도 같이 저장이 되는것을 확인 할 수 있습니다.
PERSIST 옵션에서 Team 을
TeamRepository을 이용해 삭제시
1 | |


- Team 객체는 삭제된것이 보이나. Team 에 연관관계를 맺고 있던 Member 들은 DB에서 삭제되지 않는것을 확인할 수 있습니다.
Cascade.ALL
PERSIST + REMOVE
1 | |

- Team 을 삭제시 연관관계를 맺고 있던, Member 도 같이 삭제되는것을 확인할 수 있습니다. (생명주기를 같이하게 됩니다.)
SoftDelete 적용시 주의
부모(Team) 쪽에서 반드시 SoftDelete 를 같이 구현해주어야 합니다.
1 | |
1 | |


- MyMember 에만
softDelete를 구현해둔 경우, Team 쪽에서 Cascade 로 삭제 시도시, HardDelete 로 동작해서, FK 참조 에러가 발생합니다.JPA SQL Error: 23503
1 | |
- Cascade 옵션은 하위 엔티티를 먼저 삭제하고 상위 엔티티를 삭제 하는 순서로 동작하게 됩니다.
- 하위 엔티티만 SoftDelete 인 경우, 상위 엔티티를 삭제하는 과정에서 하위 엔티티와의 FK 관계가 아직 끊어지지 않은것으로 판단되어 FK 관련 에러가 발생하게 됩니다.
참고
상위 엔티티(Team) 에만
SoftDelete가 구현되고 상위엔티티를 삭제하는경우에는, 상위 엔티티는SoftDelete, 하위 엔티티는HardDelete로 에러 없이 수행이 되어집니다.
Delete 함수 직접 구현시 주의
1 | |
1 | |


- softDelete 를 위해서 직접 delete 함수를 구현해주는 경우도 있습니다.
- Repository 가 아닌 해당 함수를 통해 삭제하는경우에는
cascade.REMOVE옵션이 작동하지 않아, 부모 객체(Team) 만 삭제되게 됩니다.
1 | |
- 이부분을 해결하기 위해서는 직접 하위 엔티티의 softDelete 컬럼값을 변경해 주어야 합니다. (MyMember에
delete()함수를 구현해주고 호출하는것이 좋으나, 직관성을 위해 코드를 위와같이 구현했습니다.)
OrphanRemoval
Cascade 의 한계
team 을 통해서 member 를 객체 관계(members)에서 끊어내는 경우 DB 에는 적용안됌
1 | |
- 객체간의 연관관계는 끊어졌으나
MyMember에 대해서 delete 쿼리가 나가지 않고, DB 상에서는 여전히 MyMember와 Team 둘다 그대로 남아있게 됩니다. - 즉 List 에서는 삭제되었으나, DB 상에서는 삭제가 되지 않습니다.
orphanRemoval = true
1 | |

orphanRemoval = true옵션을 활성화 해주는 경우, 부모 엔티티에서 자식 엔티티와의 관계를 객체상으로 끊어낼때, delete 쿼리가 발생하여 DB 상에서도 자식 엔티티가 삭제되는것을 확인할 수 있습니다.
주의할점
1 | |
1 | |
orphanRemoval = false인 경우 객체상에서 관계를 끊고, 부모 엔티티를 삭제하는 경우[SQL Error: 23503]와 함께 FK 에러가 납니다.
1 | |
- 초기 부모 객체는 자식 객체를 2개가지고 있는 상황에서 1개의 자식객체와 연관관계를 끊고,
CASCADE REMOVE옵션을 통해 부모 삭제를 시도하면 보유하고 있는 자식 객체는 1개(자식2) 밖에 없다고 판단하게 됩니다. - 자식 엔티티에 대해 1개의 DELETE 쿼리만 발생하게 되어. DB 상에 여전히 남아있는 고아 객체(자식2) 로 인해 FK 참조에러가 발생하게 됩니다.
Conclusion
CASCADE는 부모와 자식간의 생명주기를 같이하게 하는 옵션이다. (부모가 삭제되는경우 자식도 DB 삭제, 부모와 자식간의 연관관계를 맺고 부모를 저장하는 경우 자식도 같이 DB저장)orphanRemoval의 경우, 부모와 자식간의 연관관계를 DB레벨에서 같이 관리하는 옵션이다. (부모에서 자식간의 연관관계를 끊으면 자식은 고아가 되는것이 아니라 DB 상에서도 삭제)- 상황에 맞춰서 개발을 진행하되, 일반적으로는
CASCADE = ALL,orphanRemoval = true로 가져가는것을 추천한다.