[JPA] [MySQL] ON DELETE CASCADE์ ์ํํธ ์ญ์ (Soft Delete)
1. ๋ฌธ์ ์ํฉ
๊ฒ์๊ธ(Post)์ ์ญ์ ํ ๋, ๊ทธ ์์ ์๋ ๋๊ธ(Comment)๋ ํจ๊ป ์ญ์ ๋๋๋ก ๊ตฌํํ๊ณ ์ถ์๋ค.
๊ฒ์์ ํ๋ค๊ฐ @OnDelete ์ด๋ ธํ ์ด์ ์ ์๊ฒ ๋์๊ณ , ์ด๊ฒ์ ์ฌ์ฉํ๋ฉด ๋ถ๋ชจ(Post)๋ฅผ ์ญ์ ํ ๋ ์์(Comment)๋ ๊ฐ์ด ์ญ์ ๋๋ค๊ณ ํ๋ค.
๊ทธ๋์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฉํ์ง๋ง ๋ฌธ์ ๋ ๊ทธ๋๋ก ์๋ค.
constraint FKh4c7lvsc298whoyd4w9ta25cr
foreign key (post_id) references posts (id)
์ด ์ธ๋ํค์๋ ON DELETE CASCADE ์ต์ ์ด ์์๊ธฐ ๋๋ฌธ์, ๋ถ๋ชจ(Post)๋ฅผ ์ญ์ ํ๋ ค๊ณ ํ๋ฉด ์์(Comment)์ด ๋จ์์ ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Cannot delete or update a parent row: a foreign key constraint fails
(`recifit_app`.`comments`, CONSTRAINT `FKh4c7lvsc298whoyd4w9ta25cr`
FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`))
2. ์๋ํ๋ ๋ฐฉ๋ฒ: @OnDelete์ DB CASCADE
์ฐ์ ์ํฐํฐ์ @OnDelete๋ฅผ ์ถ๊ฐ ํด๋ดค๋ค.
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE) โ
private Post post;
๊ทธ๋ฆฌ๊ณ MySQL Workbench์์ ์ธ๋ํค๋ฅผ ON DELETE CASCADE๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
-- ๊ธฐ์กด FK ์ญ์ --
ALTER TABLE comments DROP FOREIGN KEY FKh4c7lvsc298whoyd4w9ta25cr;
-- ์๋ก ์์ฑ: ON DELETE CASCADE ์ถ๊ฐ --
ALTER TABLE comments
ADD CONSTRAINT FKh4c7lvsc298whoyd4w9ta25cr
FOREIGN KEY (post_id) REFERENCES posts(id)
ON DELETE CASCADE;
์ด์ ๋ถ๋ชจ(Post)๋ฅผ ์ง์ฐ๋ฉด ์์(Comment)๋ ์๋์ผ๋ก ์ญ์ ๋๋ ๊ตฌ์กฐ๊ฐ ๋์๋ค.
3. ๋ฌธ์ ์ : ์ํํธ ์ญ์ ์ ์ถฉ๋
ํ์ง๋ง Recifit ํ๋ก์ ํธ๋ ์ํํธ ์ญ์ (Soft Delete) ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
์ค์ ๋ก DELETE ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๋์ deleted_at ์ปฌ๋ผ์ ์๊ฐ์ ๊ธฐ๋กํ๋ ๋ฐฉ์์ผ๋ก ์ญ์ ์ฒ๋ฆฌํ๋ค.
์๋ฅผ ๋ค์ด, BaseEntity์๋ ๋ค์๊ณผ ๊ฐ์ ๋ฉ์๋๋ฅผ ๋๊ณ ์๋ค.
public void softDelete() {
this.deletedAt = LocalDateTime.now();
}
์ด๋ ๊ฒ ํ๋ฉด ์ํฐํฐ๋ฅผ ์ญ์ ํ ๋ ์ค์ DB์์ ํ์ ์ ๊ฑฐํ์ง ์๊ณ , deleted_at ๊ฐ๋ง ์ฑ์ ๋ฃ์ด ๋ ผ๋ฆฌ์ ์ผ๋ก ์ญ์ ๋์๋ค๊ณ ํ์ํ๋ค.
๊ทธ๋ฐ๋ฐ ON DELETE CASCADE๋ ๋ฌผ๋ฆฌ ์ญ์ ์์๋ง ๋์ํ๋ค๊ณ ํ๋ค.
๋ถ๋ชจ(Post)๋ฅผ soft delete ํด๋ DB ์
์ฅ์์๋ DELETE๊ฐ ๋ฐ์ํ์ง ์์ผ๋ฏ๋ก, Comment๋ ๊ทธ๋๋ก ๋จ๊ณ CASCADE๋ ์๋ฌด๋ฐ ๋ฐ์์ด ์๋ค.
4. ๊ฒฐ๋ก : CASCADE ์ ๊ฑฐ & ์ํํธ ์ญ์ ๋ก์ง ์ ์ฉ
๋ฐ๋ผ์ ์ธ๋ํค CASCADE๋ฅผ ์ ๊ฑฐํ๊ณ , ์ญ์ ์ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ์ง์ ๊ตฌํํ๊ธฐ๋ก ํ๋ค.
-- CASCADE ๋ถ์ธ FK ์ญ์
ALTER TABLE comments DROP FOREIGN KEY FKh4c7lvsc298whoyd4w9ta25cr;
-- CASCADE ์๋ FK๋ก ์ฌ์์ฑ
ALTER TABLE comments
ADD CONSTRAINT FKh4c7lvsc298whoyd4w9ta25cr
FOREIGN KEY (post_id) REFERENCES posts(id);
์ด์ DB๋ ๋จ์ํ ์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ๋ง ๋ณด์ฅํ๊ณ , ์ค์ ์ญ์ ์ ํ๋ ์๋น์ค ๋ ์ด์ด์์ soft delete ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
@Transactional
public void deletePost(Long postId, Long memberId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
if(!post.getMember().getId().equals(memberId)) {
throw new CustomException(ErrorCode.NO_DELETE_MODIFY_PERMISSION);
}
post.softDelete();
// Comment๋ ์ํํธ ์ญ์
List<Comment> comments = commentRepository.findAllByPostId(postId);
comments.forEach(Comment::softDelete);
}
5. ์ ๋ฆฌ
- ON DELETE CASCADE → ๋ฌผ๋ฆฌ ์ญ์ ์์๋ง ์ ํจ
- Soft Delete๋ฅผ ์ฐ๋ ํ๋ก์ ํธ์์๋ CASCADE๊ฐ ๋ถํ์ํ๊ณ ์คํ๋ ค ํผ๋๋ง ์ด๋
- ๋์ deleted_at ๊ธฐ๋ฐ์ผ๋ก ์กฐํ๋ฅผ ์ ํ(@Where(clause = "deleted_at IS NULL"))ํ๊ณ ,
- ์๋น์ค ๋ ์ด์ด์์ ์ฐ๊ด๋ ์ํฐํฐ๊น์ง ์ง์ soft delete ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ ์