Spring Data JPA ‘delete’ 논리 에러

알파로그·2023년 5월 14일
0

Error

목록 보기
27/37

✏️ 발단

  • 객체의 필드값 변화를 그래프로 만들기 위해서 Spring boot event 를 적용한 Snapshot 객체를 만들어 그래프를 구현하고 있었다.

    • 그래프는 일주일까지 기록되기 때문에 하나의 객체당 Snapshot 7 개의 객체만 있으면 되서,
      새로운 Snapshot 이 생성될 때 마다 오래된 Snapshot 을 삭제하는 로직을 구현해야 됬다.
  • 객체 구조

Memeber -< MemberSnapshot (Member 하나당 7개의 객체만 존재해야함)
  • 문제의 코드
@Transactional
public void initDbSnapshotCreate(Member member, BaekJoonDto dto, String dayOfWeek) {

    MemberSnapshot snapshot = MemberSnapshot.initDbCreate(member, dto, dayOfWeek.substring(0, 3));
    memberSnapshotRepository.save(snapshot);

    List<MemberSnapshot> snapshotList = member.getSnapshotList();
    MemberSnapshot snapshot1 = snapshotList.get(snapshotList.size() - 1);
    memberSnapshotRepository.delete(snapshot1);
}
  • 이 과정에서 MemberSnapshotRepository.delete() 가 작동하지 않는 문제가 발생했다.
    • 문제는 예외가 발생하지 않고 서버는 정상 작동하는데 data 가 삭제되지 않고 그대로 남아있다는 점이다.

✏️ 원인 파악

  • 논리 에러가 발생했기 때문에 원인을 찾을 수 있는 방법은 디버그 모드밖에 없었다.

📍 1. 하나의 Transaction 에 2번의 Query 문이 있는 경우 - 실패

🔗 Transaction 개념

  • JPA 는 Transaction 하에서 db 의 수정 작업을 할 때 명령이 끝난 즉시 db 에 반영을 하는 것 이 아닌 Trasaction 메서드가 종료되면 한번에 commit 작업을 수행한다.
    • 혹시 이 과정에서 뭔가 꼬여서 작동이 안되는게 아닐까? 하고 두 로직을 별개의 method 로 분리해봤다.
    • 변함없이 논리에러가 발생한다.
@Transactional
public void initDbSnapshotCreate(Member member, BaekJoonDto dto, String dayOfWeek) {

    MemberSnapshot snapshot = MemberSnapshot.initDbCreate(member, dto, dayOfWeek.substring(0, 3));
    memberSnapshotRepository.save(snapshot);
}

@Transactional
public void deleteSnapshot(Member member) {

    List<MemberSnapshot> snapshotList = member.getSnapshotList();
    MemberSnapshot snapshot1 = snapshotList.get(snapshotList.size() - 1);
    memberSnapshotRepository.delete(snapshot1);
}

📍 2. Member 의 변경감지가 작동하는 경우 - 실패

  • Member 객체와 Snapshot 객체는 부모 자식 관계로 join 되어 있는데,
    이 때 Snapthot 을 삭제할 경우 매핑되어있는 Member 에서는 변화가 없어 변경감지가 작동하지 않고,
    DB 를 변경하지 않게 될 수 있다.
    - 이 경우 Snapshot 객체의 삭제도 이루어지지 않는다.

  • 알아보니 deleteById 를 사용하면 해결할 수 있다고한다.

    • deleteById 는 삭제하기 전 부모 table 에서 체크하는 과정이 없기 때문에 바로 삭제된다고 한다.
    • 전혀 소용이 없었다.
@Transactional
public void initDbSnapshotCreate(Member member, BaekJoonDto dto, String dayOfWeek) {

    MemberSnapshot snapshot = MemberSnapshot.initDbCreate(member, dto, dayOfWeek.substring(0, 3));
    memberSnapshotRepository.save(snapshot);
    this.deleteSnapshot(member);
}

public void deleteSnapshot(Member member) {
    List<MemberSnapshot> snapshotList = member.getSnapshotList();
    Long snapshotId = (long) (snapshotList.size() - 1);
    memberSnapshotRepository.deleteById(snapshotId);
}

📍 3. Member 의 Snapshot list 의 인덱스를 먼저 제거 후 snapshot 삭제 - 성공

  • 앞에서 부모객체의 변화 때문에 작업 수행이 안될 수 있다는 점을 알았기 때문에 Member 의 Snapshot 부터 삭제한 후 db 에서 삭제해보기로 했다.

✏️ 해결 방법

  • Member 의 Snapshot 을 먼저 삭제해주고, delete 를 수행하니 정상적으로 삭제가 완료되었다.
@Transactional
public void initDbSnapshotCreate(Member member, BaekJoonDto dto, String dayOfWeek) {

    MemberSnapshot snapshot = MemberSnapshot.initDbCreate(member, dto, dayOfWeek.substring(0, 3));
    memberSnapshotRepository.save(snapshot);
    this.deleteSnapshot(member);
}

public void deleteSnapshot(Member member) {
    List<MemberSnapshot> snapshotList = member.getSnapshotList();
    MemberSnapshot snapshot = snapshotList.get(snapshotList.size() - 1);

    snapshotList.remove(snapshot);
    memberSnapshotRepository.delete(snapshot);
}
profile
잘못된 내용 PR 환영

0개의 댓글