회원탈퇴시, 회원이 등록한 상품을 모두 삭제하려는 작업을 진행하려는 중에 delete 쿼리를 한번만 나가게 하고자 한다.
/*
* (non-Javadoc)
* @see org.springframework.data.repository.Repository#deleteAll()
*/
@Override
@Transactional
public void deleteAll() {
for (T element : findAll()) {
delete(element);
}
}
코드를 보면 디비에서 모두 가져온 다음 하나하나 delete 쿼리를 날리는 것을 알 수 있다. 이건 사용하면 안된다는 걸 깨달았다.
/*
* (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;
}
}
@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