[JPA] deleteAll vs deleteAllInBatch, deleteAllInBatch vs @Query

이수찬·2023년 7월 27일

JPA

목록 보기
1/1

JPA에서는 여러 방식으로 데이터를 삭제할 수 있다.
그중 몇가지 방식과 차이점에 대해 알아보자.

1. deleteAll

deleteAll

@Override
	@Transactional
	public void deleteAll() {

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

위 메소드를 보면 findAll()를 통해 조회쿼리를 날린 후, 조회 결과 요소들을 순회하며, 삭제쿼리를 날리는 것을 알 수 있다.


실제 쿼리를 보면, 해당 엔티티를 조회한 후, 삭제 쿼리를 N개 날리는 것을 볼 수 있다.
즉, 쿼리가 N + 1 개 날라간다.

만약 10만개의 데이터가 있다면, 조회 1번 + 10만번의 삭제 쿼리가 나가게되어 성능에 문제가 되는 N + 1 문제가 발생한다.


2. deleteAllInBatch

 @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에 대해 먼저 알아보자.


1. deleteAllInBatch

@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 쿼리를 날릴 수 있을까?


2. @Query 사용하여 직접 입력

@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;

0개의 댓글