[JPA] deleteAll() vs deleteAllInBatch() vs @Query 사용

하스레·2023년 1월 13일
0

회원탈퇴시, 회원이 등록한 상품을 모두 삭제하려는 작업을 진행하려는 중에 delete 쿼리를 한번만 나가게 하고자 한다.

  1. deleteAll()의 내부 구현은 다음과 같다.
/*
 * (non-Javadoc)
 * @see org.springframework.data.repository.Repository#deleteAll()
 */
@Override
@Transactional
public void deleteAll() {

	for (T element : findAll()) {
		delete(element);
	}
}

코드를 보면 디비에서 모두 가져온 다음 하나하나 delete 쿼리를 날리는 것을 알 수 있다. 이건 사용하면 안된다는 걸 깨달았다.

  1. deleteAllInBatch()의 경우 내부 구현은 다음과 같다.
/*
 * (non-Javadoc)
 * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable)
 */
@Override
@Transactional
public void deleteAllInBatch(Iterable<T> entities) {

	Assert.notNull(entities, "Entities must not be null!");

	if (!entities.iterator().hasNext()) {
		return;
	}

	applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em)
			.executeUpdate();
}

여기에서, getQueryString()의 매개변수인 DELETE_ALL_QUERY_STRING을 살펴보자.

public abstract class QueryUtils {

	public static final String COUNT_QUERY_STRING = "select count(%s) from %s x";
	public static final String DELETE_ALL_QUERY_STRING = "delete from %s x";
	public static final String DELETE_ALL_QUERY_BY_ID_STRING = "delete from %s x where %s in :ids";
    
}

delete from %s x문을 통해 한꺼번에 제거되지만, QueryUtils의 다른 코드를 보면, 이를 or로 이어붙이고 있음을 알 수 있다.

public abstract class QueryUtils {
    public static <T> Query applyAndBind(String queryString, Iterable<T> entities, EntityManager entityManager) {
        Iterator<T> iterator = entities.iterator();
        StringBuilder builder = new StringBuilder(queryString);
        builder.append(" where");

        int i = 0;
        while (iterator.hasNext()) {
            iterator.next();
            builder.append(String.format(" %s = ?%d", alias, ++i));
            if (iterator.hasNext()) {
                builder.append(" or"); 
            }
        }
        Query query = entityManager.createQuery(builder.toString());

        return query;
    }
}
  1. @Query 사용하여 직접 입력
@Modifying
@Query(value = "delete from Product p where p.user.userId=:userId")
void deleteProductsByUserId(@Param("userId") Long userId);

이를 통해 or 대신 in 절을 사용하여 delete 쿼리를 한번 날릴 수 있었다.
in절을 선택한 이유는 많은 양의 데이터를 조회하는 경우 or을 연속해서 사용하는 것보다 훨씬 빠르기 때문이라고 한다. 쿼리 실행계획을 살펴보면 or절은 ALL, in절은 range를 타는 것을 확인할 수 있다고 한다.(참고)


참고
https://blog.yevgnenll.me/posts/jpa-use-not-deleteall-but-deleteallinbatch
https://yeonyeon.tistory.com/286

profile
Software Developer

0개의 댓글