인프런 커뮤니티 질문&답변

플레어님의 프로필 이미지
플레어

작성한 질문수

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

deleteAllByMemberId 결과확인 문의(삭제가 안됨)

해결된 질문

작성

·

292

0

Member, MemberAuth 테이블이 있습니다.

Member (1) : MemberAuth (N) 의 관계이고, MemberAuth의 데이터를 삭제하려고 아래와 같이 했는데 실제로 삭제가 되지 않네요. 서비스에서 삭제결과는 제대로 디버깅에서 확인이 되는데요.

MemberAuthRepository.java

@Repository
public interface MemberAuthRepository extends JpaRepository<MemberAuth, Long> {
List<MemberAuth> findByMemberId(Long memberId);
int deleteAllByMemberId(Long memberId);
}

MemberService.java : changeRole에서 기존 Role을 삭제하는 부분입니다.

    @Transactional
public void changeRole(MemberDto memberDto, List<MemberAuthDto> memberAuthDtos) throws Exception {
Member member = memberRepository.findByEmail(memberDto.getEmail());
if(member==null) {
throw new UsernameNotFoundException(memberDto.getEmail());
}
//기존 ROLE을 찾아서 모두 지운다.
int iDel= memberAuthRepository.deleteAllByMemberId(member.getId());

//ToDo :: 넘어온 ROLE 을 생성한다.

}

디버깅으로 보면 iDel 값이 확인이 됩니다. 그런데 데이터베이스 확인하면 값이 지워지지 않고있네요. 어찌해야 하는걸까요? 

답변 6

1

플레어님의 프로필 이미지
플레어
질문자

정말 감사합니다. 즐거운 주말 보내세요~

0

김영한님의 프로필 이미지
김영한
지식공유자

확인해보니 제가 설명에서 누락한게 있네요^^ 다음 부분을 넣어주시면 됩니다.

class Member {

    @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)

    List<MemberAuth> memberAuths = new ArrayList<>();

}

이렇게 orphanRemoval까지 넣어주면 Member엔티티 만으로 memberAuth의 라이프사이클을 관리할 수 있습니다.

감사합니다

0

플레어님의 프로필 이미지
플레어
질문자

답변 감사합니다.~

일반적으로 cascade 사용해서 알려주신 방식으로 처리하는것이 각각 제어하는것보다 좋은 방법인거죠? 아무래도 쿼리위주로 개발하던 방식과는 달라서 생소하긴 하네요.

위 수정된 부분 적용해서 수행해봤는데요, 제가 뭘 잘못한게 있는지 잘 안돼서 메일로 제가 수정한 파일 3개 첨부해서 드렸습니다. 함 확인 부탁드립니다. 

즐거운 하루 보내세요~

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요^^

혹시나 했는데, 역시나 이 문제였네요.

member -> memberAuth는 지금 cascade 관계입니다.

그런데 memberAuth를 삭제할 때 deleteAll을 호출하셨는데요. 여기서 충돌이 발생합니다.

deleteAll을 호출하면 em.remove(memberAuth)가 호출됩니다.

그런데 객체 연관관계 그러니까 member.memberAuth는 모두 남아있는 상태입니다. (em.remove를 호출한다고 실제 자바 객체가 삭제되지는 않습니다.)

결국 JPA는 member와 memberAuth가 남아있으니 cascade를 해서 두 객체를 함께 저장해야 하는 것과 em.remove(memberAuth)를 호출한 것이 충돌하는 것이지요.

사실 이렇게 복잡하면 뭔가 잘못된 것입니다.

cascade를 사용하면 해당 객체의 라이프사이클을 member를 통해서 제어해야 합니다. 따라서 중간에 다음 코드를 넣으시면 원하는데로 작동합니다. 그리고 deleteAll이나 deleteInBatch 등등은 사용하지 않아야 합니다.

변경 메서드() {

member.removeAuth(AuthType.valueOf(befRole));

}

class member {

...

    public void removeAuth(AuthType authType) {

        List<MemberAuth> result = memberAuths.stream()

                .filter(m -> m.getAuthType().equals(authType))

                .collect(Collectors.toList());

        memberAuths.removeAll(result);

    }

}

다른 설계 방법은 cascade를 사용하지 않고 각각 제어하는 것입니다. (지금처럼 memberAuthRepository로 제어)

감사합니다.

0

플레어님의 프로필 이미지
플레어
질문자

메일전송이 계속 실패해서 구글드라이브 공유링크로 전달드렸습니다.

확인부탁드려요.. 감사합니다.~

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 조호형님

제가 정확하게 도움을 드리려면 동작하는 코드가 있으면 좋겠어요.

동작하는 코드와 현재 상황을 재현할 수 있는 test 케이스를 넣어서 전체 프로젝트를 압축해서 올려주세요.

감사합니다.

플레어님의 프로필 이미지
플레어

작성한 질문수

질문하기