DataIntegrityViolationException란?
DataIntegrityViolationException은 데이터 무결성 제약 조건을 위반했을 때 발생하는 에러다.
나는 JPA에서 Board 엔티티와 Comment 엔티티를 다음과 같이 매핑했다.
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Board {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "BOARD_ID")
private Long board_id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
private String board_name;
private String board_content;
private int board_click;
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
public void incrementClick() {
this.board_click += 1; // 조회수를 1 증가시킴
}
// 양방향 편의 메서드
public void changeMember(Member member) {
this.member = member;
member.getBoards().add(this);
}
}
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Comment {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "COMMENT_ID")
private Long comment_id;
private String comment_content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "BOARD_ID")
private Board board;
// 양방향 편의 메서드
public void addBoard(Board board) {
this.board = board;
board.getComments().add(this);
}
}
댓글과 게시판의 관계는 N:1 이다. 댓글이 게시판을 FK로 받고 있으므로 연관관계의 주인은 댓글이 된다.
나는 Board가 삭제되면 댓글도 다 삭제되길 원해서 casecade.ALL 설정을 걸어두었다.
에러발생 원인
/**
* 게시판 삭제
*/
@Transactional
public void deleteBoard(Long id) {
Board board = em.find(Board.class, id);
if (board == null) {
throw new NotFindBoardException("해당 게시물은 존재하지 않습니다.");
}
em.createQuery("delete from Board b where b.board_id = :boardId")
.setParameter("boardId", id)
.executeUpdate();
}
위의 코드대로 jpql을 이용하여 boardId를 받아서 게시판을 삭제하려니까 아래와 같은 에러가 발생했다.
org.springframework.dao.DataIntegrityViolationException: JDBC exception executing SQL [delete from board where board_id=?] [Referential integrity constraint violation: "FKLIJ9OOR1NAV89JEAT35S6KBP1: PUBLIC.COMMENT FOREIGN KEY(BOARD_ID) REFERENCES PUBLIC.BOARD(BOARD_ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from board where board_id=? [23503-224]] [n/a]; SQL [n/a]; constraint ["FKLIJ9OOR1NAV89JEAT35S6KBP1: PUBLIC.COMMENT FOREIGN KEY(BOARD_ID) REFERENCES PUBLIC.BOARD(BOARD_ID) (CAST(1 AS BIGINT))"; SQL statement:
에러를 살펴보면 casecade 설정이 되어있어서 board 객체를 삭제하려고 할 때 게시판 하위에 있는 댓글들이 남아 있어서 제약 조건 위배로 에러가 발생했습니다.
이를 해결하기 위해서는 board 엔티티를 삭제하기 전에 게시판 하위의 댓글들을 먼저 다 지워주거나 엔티티 매니저를 사용해서 board 객체를 지워 casecade가 발동되도록 만들어야 한다.
해결방법
수정된 코드
/**
* 게시판 삭제
*/
@Transactional
public void deleteBoard(Long id) {
Board board = em.find(Board.class, id);
if (board == null) {
throw new NotFindBoardException("해당 게시물은 존재하지 않습니다.");
}
em.remove(board);
}
jpql은 엔티티의 설정 casecade 같은 설정들을 반영하지 않는다. jpql은 그냥 데이터베이스 레벨에서 객체를 대상으로 하나의 쿼리문을 날리는 것에 불과하다. 그냥 행삭제만 의미하지 casecade 같은 엔티티간의 연관관계 설정들은 고려하지 않는다.
엔티티 매니저는 엔티티 생명주기를 관리하게 되는데 이때 엔티티의 상태를 감지하고 DB와 연관된 엔티티를 함께 관리하도록 되어 있다. 따라서 엔티티 매니저를 이용하여 board 엔티티를 삭제해야만 casecadeALL이 적용되어 댓글이 함께 연쇄삭제 되는 것이다.
이와 관련된 JPA 문서의 일부를 인용하면 다음과 같다.
“By default, the remove operation applies only to the entity object passed to the remove operation and does not cascade to related entities. Therefore, if a cascaded delete operation is desired, it is necessary to specify this using the cascade attribute of the relationship annotation.”
즉, 엔티티 간의 연관관계에서 삭제를 연쇄적으로 수행하려면 casecade 설정을 사용해야하며, 이는 엔티티 매니저의 remove 메서드를 통해서만 제대로 작동한다.
'Spring > JPA' 카테고리의 다른 글
[JPA] 스프링부트 Spring Boot JPA - Controller에서 예외처리가 안될 때 (0) | 2024.06.05 |
---|