[JPA] 부모-자식 데이터 삭제(+성능)

DB 설계 시 자식 테이블에 부모의 PK를 소유하고 있고, 부모 삭제 시 여러 가지의 제약조건을 통해 PK의 값을 null로 변경해 주거나, PK를 소유한 자식의 데이터를 삭제 등... 여러 동작을 할 수 있다. 하지만 jpa에서는 자식 데이터를 조회해 값을 수정한 다음 저장하는 방식으로 동작해야 한다.  이에 변경감지에 의한 업데이트, 벌크 업데이트에 대해 알아보자.

 

변경감지 업데이트

변경감지란 엔티티가 조회 또는 저장시 영속성 컨텍스트의 1차 캐시에 저장이 되고 트랜잭션 전 flush 동작시 1차 캐시에 저장된 값과 현재의 값을 비교해 업데이트 쿼리를 생성 후 실행하는 것을 의미한다.

 

List<Todo> todos = todoRepository.findAllByProjectAndStatusOrderByEndDateAsc(project, TodoStatus.INCOMPLETE);

// 반복문으로 null 설정하기
todos = todos.stream().map(todo-> {todo.setProject(null);
    return todo;
}).collect(Collectors.toList());

projectRepository.delete(project);

 

위의 코드는 Project 엔티티를 삭제하기 전에 Todo의 Project를 null로 수정하는 코드이다. 해당 코드를 실행시키면 프로젝트 삭제가 성공적으로 수행되지만, Todo의 개수만큼 업데이트 쿼리가 실행되는 상황이 발생한다. 만약 todo가 고정된 적은 개수를 가지고 있다면 큰 문제는 없겠지만 많은 데이터를 가지고 있으면 추후 성능상의 문제가 발생할 수 있다.

변경감지 업데이트 성능 테스트

ProjectId 1를 갖는 Todo 554개의 더미 데이터를 생성해서 삭제 테스트를 진행한 결과 총 5.07초의 응답 시간을 확인 할 수 있었고 이는 Todo의 개수가 많아질수록 성능에 더욱 악영향을 끼친 것을 확인할 수 있었다.

TODO 개수
Project 삭제시 응답 시간(변경감지)

 

벌크 업데이트

벌크업데이트는 변경감지가 아닌 @Query와 @Modifying를 사용해야 합니다.

@Modifying
@Query("update Todo t set t.project = null where t.project.id = :projectId")
int updateTodo(Long projectId);

 

벌크 업데이트 성능 테스트

위와 같은 조건인 Todo 개수 554개의 테이터를 생성하고 프로젝트 삭제 테스트시 153 ms의 응답시간을 확인 할 수 있었고, 변경감지를 이용한 것보다 굉장히 빠른 성능을 보이는 것을 확인 할 수 있다.

Project 삭제시 응답 시간(벌크 업데이트)

 

벌크 업데이트시 주의 사항

벌크 업데이트가 변경감지보다 성능적으로 빠르지만 사용할 때 주의 사항이 존재한다.

  • 벌크 업데이트는 조회 또는 저장이 아니기에 영속성 컨텍스트를 거치지 않고 데이터 베이스에 쿼리문을 보낸다.
  • 만약 업데이트할 데이터를 조회해야한다면 벌크 업데이트 후 엔티티 매니저를 통해 영속성 컨텍스트를 clear 한 뒤
  • 업데이트 된 데이터를 조회하면 수정된 데이터를 조회 할 수 있다.