DevelopmentTools/jpa

[JPA] deleteAll과 deleteAllInBatch의 차이

수짱수짱 2023. 10. 19. 02:59

UserController.java

@DeleteMapping("/carts")
    @ResponseStatus(NO_CONTENT)
    public KurlyResponse<Void> removeProduct(
            @AuthenticationPrincipal User user,
            @RequestBody RemoveCart.Request removeProductList
    ) {
        userService.removeProductList(removeProductList, user.getId());
        return KurlyResponse.noData();
    }

 

UserService.java

	 	@Transactional
    public void removeProductList(RemoveCart.Request removeProductList, Long userId) {
        for (Long productId : removeProductList.products()) {
            removeProduct(productId, userId);
        }
    }

피드백을 받기 전의 코드의 흐름은 위와 같다.

controller에서 removeProductList의 인자로 List를 넘겨주는데.. product라는 List<Product> 자료형이있고 이를 for문으로 삭제하는 코드였다 (이땐 왜 이렇게 짯는지..)

⇒ 하지만 이렇게 한다면 @Transaction에 따라 반복문이 실행될 때 마다 수많은 트랜잭션이 발생하게되어 문제가 발생할 수 있다

트랜잭션을 많이 사용하게 된다면 자원 고갈, 트랜잭션 충돌, 성능 저하등의 문제가 발생할 수 있으므로 조심!!

 

그래서, 반복문 대신에 어떻게 데이터를 한 번에 삭제할 수 있을까?를 생각해 보았다.

생각난 것은 JPA의 deleteAll() 메서드를 생각해냈고 동시에 deleteAllInBatch()와의 차이가 뭘까를 생각해 보았다.

결론을 먼저 말하자면 성능상 이점으론 deleteAllInBatch()이 유리하기 때문에 해당 메서드를 사용해서 코드를 수정하였다.

 

수정 후 코드

JPA의 deleteAllInBatch()을 사용해서 삭제할 cart의 id를 인자로 받아 cart 객체 list를 받고 삭제하는 흐름으로 변경하였다

 

그럼, deleteAll과 deleteAllInBatch의 차이는?

둘을 비교해 보기위해 org.springframework.data.repository.CrudRepository에 정의 되어 있는 CrudRepository 인터페이스를 구현한 구현체인

org.springframework.data.jpa.repository.support.SimpleJpaRepository 줄여서 SimpleJpaRepository class 내부를 알아 볼 것이다.

 

deleteAll

내부에 entity 하나씩 반복문을 돌며 delete를 호출하고 있다.

그렇다면 delete 내부는 어떻게 구현되어 있을까?

delete는 내부적으로 Entitymanger를 호출하여 find 후 remove하고 있다.

즉, 한 번에 모든 데이터를 삭제하는 것이 아니라 리스트를 받아도 1개씩 조회 후 1개씩 삭제하는 내부 로직을 가지고 있다

→ 데이터가 십만개가 넘는다면..? 십만개를 하나씩 삭제하니깐 성능상 후덜덜

 

만약 deleteAll로 지우고자 하는 데이터가 2개라면 아래와 같이 delete 쿼리가 2번 나갈 것이다

그럼 지우고자 하는 데이터가 십만개일땐 delete 쿼리가 십만개 발생하겠쥬

delete 
from
	table
where
	id=?

delete 
from
	table
where
	id=?

deleteAll을 사용하면 이러한 성능상 저하가 일어나는데 이를 위해 deleteAllInBatch가 탄생하게 됐다.

 

deleteAllInBatch

얘는 deleteAll과 다르게 org.springframework.data.jpa.repository.JpaRepository 인터페이스에 정의되어 있다.

→ 따라서 deleteAll은 mybatis나 jdbctemplate등 기술에 관련없이 사용가능 하지만 deleteAllInBatch는 spring data jpa에 포함되어 있어 반드시 JPA를 통해서만 사용이 가능하다.

 

Batch라는 뜻은 뭘까?

여러 작업이나 항목을 모아서 처리하거나 그룹화하는 행위나 방법을 나타내는 용어라고 한다.

그렇다면, deleteAllInBatch는 모아서 한 번에 삭제한다는 의미로 해석이 될 수 있다.

 

이젠 deleteAllInBatch의 내부를 알아보자.

내부 구현은 deleteAll과 마찬가지로 SimpleJpaRepository에 구현되어 있다.

내부의 applyAndBind 함수를 호출한다.

DELETE_ALL_QUERY_STRING는 "delete from %s x"; 과 entity의 이름

(그럼 해당 테이블에 대한 delete 쿼리가 getQueryString을 통해 인자로 넘어가겠지)

두번째 인자로는 엔티티 list, 그리고 세번째 마지막 인자로는 em을 넘겨준다.

applyAndBind를 보면 반복문을 돌려서 다음 값이 있을 때 or을 붙여 데이터 갯수 만큼 추가한다.

그리고 이렇게 만들어진 문자열을 build에 계속 append하여 최종적으로 createQuery를 통해 쿼리를 만든다.

이후 setParameter를 통해 값에 맞춰 데이터 번호를 넣어준다.

그리고 마지막으로 deleteAllInBatch의 executeUpdate를 실행하여 쿼리를 호출하는 것이다.

 

즉, deleteAllInBatch는 EntityManager의 createQuery()로 쿼리를 준비하여 직접 excuteUpdate()를 호출하여 한번의 쿼리로 여러개의 삭제를 진행한다.

출처:  https://mingg123.tistory.com/117

쿼리에서 볼 수 있듯이 삭제 과정 이전에 find하는 과정 없이 오로지 delete만 하는 쿼리가 1번만 나간다.

 

정리


Reference