본문 바로가기

Spring Boot

Hibernate 부모-자식 연관관계 삭제 문제

728x90

개요

조직-부서 관계를 가진 Entity를 개발 중에 발생한 문제였습니다.
조직은 여러 개의 부서를 가질 수 있는 다대 다 관계의 모델을 구성했는데, 조직이 삭제되면 하위에 있는 모든 부서가 삭제되어야 하는 기능입니다.

1
2
3
4
@OneToMany(mappedBy = "organization", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonBackReference
private List<Department> departments = new ArrayList<>();
cs

위와 같이 구현을 완료하고 삭제 버튼을 클릭하는 순간 DataIntegrityViolationException이 떨어지는군요.
분명 API에서 하라는 대로 했는데.. 이게 무슨 말도안돼는..?

그래서 작성한 가이드입니다.

개발 환경

Spring Boot 1.5.19.RELEASE
Mariadb 10.1.36


해결 방법

본 포스트에서는 예제보다 결론을 먼저 보여드리는 것이 좋은 방법일 것 같아 해결책을 먼저 제시합니다.


결론적으로는 Database Dialect를 잘못 사용하였습니다.
제가 구축하는 시스템에서는 Mariadb의 InnoDB를 사용하고 있는 데, 설정은 MySQLDialect로 되어 있더군요.

이게 무슨 말이냐 하면, 해당 클래스를 들여다 보면 쉽게 이해 할 수 있게 됩니다.

아래는 MySQLDialect.java의 supportsCascadeDelete() 메소드 입니다.
1
2
3
4
@Override
public boolean supportsCascadeDelete() {
  return false;
}
cs


아래는 MySQLInnoDBDialect.java의 supportsCascadeDelete() 메소드 입니다.

1
2
3
4
@Override
public boolean supportsCascadeDelete() {
  return true;
}
cs


네, 결론적으로 MySQLDialect는 @OnDelete 어노테이션을 지원하지 않습니다. 제가 백날 함께 지워라.. 라고 정의한다 한들 DB에 접속해보면 한결같은 마리아디비님이시죠.



그러므로 InnoDB를 사용할 때는 application.properties의 dialect를 아래와 같이 변경해 줍니다.


1
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
cs


적용 후 Spring Boot를 재시작 하면 아래와 같이 ON DELETE : CASCADE로 변경되어 있는 것을 보실 수 있습니다.





예제 코드

1. Organization.java

A. 조직은 여러 개의 부서를 포함하고 있습니다.
B. 조직이 삭제되는 경우 @OnDelete에 의해 FK로 연결된 하위의 Department 데이터도 함께 삭제합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Getter
@Setter
@Entity
@Table(name = "organization")
public class Organization {
 
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
 
  @OneToMany(mappedBy = "organization", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
  @OnDelete(action = OnDeleteAction.CASCADE)
  private List<Department> departments = new ArrayList<>();
}
 
cs



2. Department.java

부서는 조직에 속해있으며, 조직이 삭제될 때 함께 삭제되어야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
@Setter
@Entity
@Table(name = "department")
public class Department {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
 
  @ManyToOne
  @JoinColumn(name = "organization_id")
  private Organization organization;
}
 
cs











강윤구 / 대표

Yungu Kang / CEO

yungu@userinsight.co.kr


728x90