chae._.chae

[Error] java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails 본문

Error

[Error] java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails

walbe0528 2022. 7. 15. 21:17
728x90
반응형

[Error 문구]

java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails

 

 

meeting 테이블의 데이터를 삭제하려고 시도했는데 위와 같은 에러가 나면서 정상 실행이 되지 않았다. 

JPA delete관련 에러로, 연관관계가 맺어진 데이터라서 삭제되지 않는 것으로, 지우고 싶은 데이터와 연결되어 있는 다른 테이블의 데이터를 전부 지워주면 해결된다. 

 

@Transactional
public String deleteMeeting(User user, Meeting meeting){
    System.out.println("User :" + user.getUsername() + ", " + "Meeting : " + meeting.getName());
    meetingRepository.delete(meeting);
    System.out.println("모임 삭제됨");

//    System.out.println("큐앤에이, 해시태그, 미팅유저 삭제됨");  // 모임 삭제 -> meetingUser전부, 해시태그, 큐앤에이
    return "모임 삭제 완료";
}

 

처음엔 JPA 영속성 전이에 대해 제대로 이해하지 않고 무작정 코드를 짜서 이상한 코드를 작성했다. 

meeting을 삭제하면 meeting과 연관관계가 맺어진 adminQnA, meetingUser, HashTag도 함께 지워져야 하기에

meetingRepository.delete() , adminQnA.delete(), meetingUser.delete() .. 로 일일이 모두 삭제해주려 했다.

(왜냐면 그냥 meetingRepository.delete()하면 연관된 다른 데이터가 지워지지 않았기에..)

 

지워지지 않았던 이유는 내가 연관관계를 잘못 맺어줘서 한번에 삭제가 되지 않았던 것이고,

다시 설정해주니 부모 엔티티인 meeting만 삭제해도 자식 엔티티도 함께 삭제되는 것을 확인할 수 있었다.

내가 잘못 작성한 부분은 mappedBy와 JoinColumn...

 

 

내가 설계해준 엔티티 관계들이다.

 

Meeting : AdminQnA = 1 : 1 양방향

Meeting : MeetingUser = 1 : N 단방향 (Meeting과 User가 다대다 관계라 중간 엔티티인 MeetingUser를 만들어주었다)

Meeting: HashTag = 1 : 1 양방향

AdminQnA : UserQnA = 1 : N 양방향

 

< Meeting.java >

public class Meeting {

    @OneToMany(mappedBy = "meeting", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonBackReference
    private List<MeetingUser> users = new ArrayList<>();

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "hashTag_id")
    private HashTag hashTag;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "adminQnA_id")
    private AdminQnA adminQnA;
}

 

 

 

< MeetingUser.java >

public class MeetingUser {

    @ManyToOne
    @JoinColumn(name = "meeting_id")
    private Meeting meeting;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

 

< AdminQnA.java >

public class AdminQnA {

    @OneToMany(mappedBy = "adminQnA", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<UserQnA> userQnA;

    @OneToOne(mappedBy = "adminQnA", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Meeting meeting;
}

 

< UserQnA.java >

public class UserQnA {

    @ManyToOne
    @JoinColumn(name = "adminQnA_id")
    private AdminQnA adminQnA;

}

 

 

* 영속성 전이(cascade)란 부모 엔티티가 영속화될때, 자식 엔티티도 같이 영속화되고 부모 엔티티가 삭제 될때, 자식 엔티티도 삭제되는 등 부모의 영속성 상태가 자식에게 전이되는 것을 이야기한다. 영속성전이의 종류로는 ALL, PERSIST, DETACH, REFRESH, MERGE, REMOVE등이 있다. 

 

 

[CascadeType]

  • ALL → 모든 Cascade 적용
  • PERSIST → 엔티티를 영속화할 때, 연관된 엔티티도 함께 유지
  • MERGE → 엔티티 상태를 병합(Merge)할 때, 연관된 엔티티도 모두 병합
  • REFRESH → 상위 엔티티를 새로고침(Refresh)할 때, 연관된 엔티티도 모두 새로고침
  • DETACH → 부모 엔티티를 detach() 수행하면, 연관 엔티티도 detach()

"cascade = { CascadeType.PERSIST, CascadeType.MERGE}" 처럼 여러개의 속성을 한번에 지정할 수 있고, 모두 적용하고 싶다면 ALL을 사용한다. 

@OneToMany, @ManyToOne의 어노테이션에 Cascade 옵션을 설정하여 사용 가능하다.

영속성 전이는 연관관계의 주인에 관계없이 전이된다.

 

 

* 고아객체

고아객체는 부모와의 연관관계가 끊어진 자식 엔티티를 말하는 용어이다.

JPA에서는 "orphanRemoval = true" 옵션을 통해 고아객체를 자동삭제하는 기능을 제공한다. (주로 OneToMany에서 사용한다.)

부모 엔티티에서 자식 엔티티의 생명주기를 관리 할 수 있게 해주는 기능이다.

부모가 제거되면 자식은 참고가 제거되기에 고아가 되기때문에 자식도 함께 제거가 되는데, cascade에 REMOVE속성과 동일하다.(고아객체가 되는 순간 Delete 쿼리가 나간다)

@OneToMany, @OneToOne에서만 사용 가능하다. 

 

하나 또 느낀 점은 디자인이 모두 나오지 않은 상태에서 개발을 먼저 했고(테이블 설계), 화면 디자인이 나오고 기능에 맞게 엔티티를 수정하려니 바꿔야할 부분이 정말 많다.... 엔티티를 수정하고 POSTMAN으로 테스트해보면 연관관계 잘못 맺어준 것 때문에 기존에 만들어둔 API가 정상실행이 안되고....😭

연관관계는 정말 중요한 것 같고, 어렵다.... 방학에 잘 공부해봐야징

 

 

728x90