Dev/Java | Spring

Entity에서는 변경이 되었는데 DB에는 적용이 안되는 경우

vanss 2022. 9. 21. 00:54
해당 글은 경험했던 트러블 슈팅 내용을 정리하기 위한 글입니다. 어떤 피드백이든 환영이에요! 😍

상황

- 크루 장인 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 변경 순서에 대한 숙지가 덜 되어 발생했던 문제였다.