JPA에서는 여러 방식으로 데이터를 삭제할 수 있다.
그중 몇가지 방식과 차이점에 대해 알아보자.
deleteAll
@Override
@Transactional
public void deleteAll() {
for (T element : findAll()) {
delete(element);
}
}
위 메소드를 보면 findAll()를 통해 조회쿼리를 날린 후, 조회 결과 요소들을 순회하며, 삭제쿼리를 날리는 것을 알 수 있다.

실제 쿼리를 보면, 해당 엔티티를 조회한 후, 삭제 쿼리를 N개 날리는 것을 볼 수 있다.
즉, 쿼리가 N + 1 개 날라간다.
만약 10만개의 데이터가 있다면, 조회 1번 + 10만번의 삭제 쿼리가 나가게되어 성능에 문제가 되는 N + 1 문제가 발생한다.
@Override
@Transactional
public void deleteAllInBatch() {
em.createQuery(getDeleteAllQueryString()).executeUpdate();
}
private String getDeleteAllQueryString() {
return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());
}
public static final String DELETE_ALL_QUERY_STRING = "delete from %s x";
코드를 보면 deleteAll과 달리 한방 쿼리를 통해서 삭제하는 것을 알 수 있다.

실제 쿼리 또한 delete쿼리를 1개만 날려 테이블에 있는 데이터를 모두 삭제하는 것을 알 수 있다.
deleteAll과 달리 단 1개의 쿼리를 통해 같은 기능을 수행한다.
delete 관련 기능이 필요한경우, 성능을 고려해서 deleteAllInBatch() 를 사용하는 것이 좋아 보인다.
그럼 만약 테이블의 모든 데이터를 삭제하는 것이 아닌, 원하는 데이터만 삭제할 때는 어떻게 해야할까?
우선 deleteInBatch에 대해 먼저 알아보자.
@Override
@Transactional
public void deleteAllInBatch() {
em.createQuery(getDeleteAllQueryString()).executeUpdate();
}
@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();
}
코드를 보면, 삭제하고 싶은 Entity들을 전해주고 applyAndBind메소드를 사용한다.

그러나 실제 쿼리를 보면, in 쿼리가 아닌 or 쿼리를 이용하는 것을 알 수 있다.
applyAndBind
while (iterator.hasNext()) {
iterator.next();
builder.append(String.format(" %s = ?%d", alias, ++i));
if (iterator.hasNext()) {
builder.append(" or");
}
}
applyAndBind 코드를 보니 내부에서 "or"를 통해 쿼리를 만든 것을 알 수 있었다.
하지만 실제 메소드를 작성하면서 생각한 결과와는 다른 or를 사용한 쿼리가 날라갔다.
그럼 어떻게 해야 in 쿼리를 날릴 수 있을까?
@Modifying
@Query(value = "delete from Item i where i.member.memberId=:memberId")
void deleteItemByMemberIds(@Param("memberId") Long memberId);
위와 같이 @Query를 통해 직접 쿼리를 작성하면 or 대신 in 절을 사용하여 delete 쿼리를 한번 날릴 수 있었다.
in절을 사용하여 많은 양의 데이터를 조회하는 경우 or을 연속해서 사용하는 것보다 속도가 훨씬 빠르다.
쿼리 실행계획을 살펴보면 or절은 ALL, in절은 range를 타는 것을 확인할 수 있다고 한다
in절을 선택한 이유는 많은 양의 데이터를 조회하는 경우 or을 연속해서 사용하는 것보다 훨씬 빠르기 때문이다.
쿼리 실행계획을 살펴보면 or절은 ALL, in절은 range를 타는 것을 확인할 수 있다.
explain delete from item where id = 1 and 2;

explain delete from item where id = 1 or 2;
