해당 글은 경험했던 트러블 슈팅 내용을 정리하기 위한 글입니다. 어떤 피드백이든 환영이에요! 😍
상황
- 크루 장인 Member가 Crew를 제거했을 때, Member의 Crew관련 권한을 제거해주고자 하는데, Entity에서는 변경이 되는 것 같지만 DB에는 적용이 안되고 있다. DB에 적용시켜야 한다.
//Crew장의 권한 제거 메서드
public void removeCaptainAuthority() {
this.crewInfo = new CrewInfo(false, null);
}
1차시도
- memberRepository.save()를 통해 JPA의 쿼리를 날려보면 어떨까? → 참조 무결성 제약 조건을 위반 (Referential integrity constraint violation) 발생!! 왜지?? 아마 memberRepository.save()에서 참조되어있는 Crew를 못찾게되어 그런 것 같다.
원인
- 위 코드에서 메서드 순서에 따른 Entity Member의 crewId와 DB Member의 crew_id 상태 변화를 보자
memberRepository.updateCrewIdOfMembers(crewId)는 하기 코드와 같이 DB에 쿼리를 날리는 메서드이다.
@Modifying(clearAutomatically = true)
@Query("update Member m set m.crew.id = null where m.crew.id = ?1")
void updateCrewIdOfMembers(Long crewId);
memberRepository.updateCrewIdOfMembers(crewId)
// 1. DB Member의 crew_id 상태 : 4 -> null
crewRepository.delete(crew)
// 2. crew_id 4에 해당되는 Crew DB에서 제거
member.removeCaptainAuthority();
// 3. Entity Member의 crewId 상태 : 4
memberRepository.save(member);
// 4. crewId 값이 4인 Entity를 저장 시도 -> 해당되는 crew는 2번에서 제거되었으므로 에러발생
- 위와 같이, Member가 참조하고 있는 Crew가 삭제되어있는 상태에서 해당 Crew를 참조하고 있는 상태의 Member Entity를 저장하면서 ERROR 가 발생한것으로 보인다.
그러므로, 참조하고있는 Crew 데이터를 DB에서 유지하면서 삭제도 하기 위해서 하드 delete에서 소프트 delete로 변경!
2차시도
- Crew에 Boolean deleted 값을 추가하고 소프트 delete로 진행해보자
@Transactional
public void deleteCrew(Long crewId, CrewDeleteRequest crewRequest) {
Member member = validMemberIsExist(crewRequest.getMemberId());
validCaptainAuthority(crewId, member, member.getCrew().getId());
Crew crew = validIsCrewExist(crewId);
member.removeCaptainAuthority();
memberRepository.updateCrewIdOfMembers(crewId);
memberRepository.save(member);
crew.delete(); // Soft delete로 Crew의 상태값 변경
crewRepository.save(crew);
}
- Member의 Crew관련 권한은 변경되었지만 crew_id가 다시 살아나버렸다.. ㅠ 왜일까 메서드 순서 때문일까? 다시 코드 작동 순서를 살펴보자.
@Transactional
public void deleteCrew(Long crewId, CrewDeleteRequest crewRequest) {
Member member = validMemberIsExist(crewRequest.getMemberId());
validCaptainAuthority(crewId, member, member.getCrew().getId());
Crew crew = validIsCrewExist(crewId);
member.removeCaptainAuthority(); // 1. Entity Member의 Crew 권한 상태 변경(crewId = 4)
memberRepository.updateCrewIdOfMembers(crewId); // 2. DB Member의 crew_id 4 -> null값으로 변경 (crewId = 4 -> null)
memberRepository.save(member); // 3. 1번 상태의 Entity Member 저장
crew.delete(); // Soft delete로 Crew의 상태값 변경
crewRepository.save(crew);
}
- 예상한 대로 메서드 순서가 잘못되었다. 2번에서 DB crew_id를 null값으로 바꿔주지만 3번 save() 과정에서 다시 crew_id = 4로 바꿔버린다. 그럼 save() 이후에 crew_id를 null값으로 바꾸는 쿼리를 날려보자
3차시도
- 하기 코드처럼 메서드 순서를 바꾸고 진행해보자
@Transactional
public void deleteCrew(Long crewId, CrewDeleteRequest crewRequest) {
Member member = validMemberIsExist(crewRequest.getMemberId());
validCaptainAuthority(crewId, member, member.getCrew().getId());
Crew crew = validIsCrewExist(crewId);
member.removeCaptainAuthority(); // 1. Entity Member의 Crew 권한 상태 변경(crewId = 4)
memberRepository.save(member); // 2. 1번 상태의 Entity Member 저장
memberRepository.updateCrewIdOfMembers(crewId); // 3. DB Member의 crew_id 4 -> null값으로 변경 (crewId = 4 -> null)
crew.delete(); // Soft delete로 Crew의 상태값 변경
crewRepository.save(crew);
}
- Baaam!! 크루장이었던 Member의 권한도 모두 변경되었고 crew_id도 null처리되었고, 삭제된 Crew의 deleted 값도 TRUE로 정상 변경되었다.!!! 🎉
결론
- 우선 처음 문제였던 DB에 변경된 값이 적용이 안되던 것은 변경 감지가 안되어서 그랬던 것이라 save를 별도로 해줘야 했고, Entity의 변경과 쿼리로 인한 DB 변경 순서에 대한 숙지가 덜 되어 발생했던 문제였다.
'Dev > Java | Spring' 카테고리의 다른 글
연관관계 맵핑되어 있는 객체 삭제 시, 에러가 발생하는 경우 (0) | 2022.09.20 |
---|