본문 바로가기
문제해결

JPA에서 deleteAll 메서드 사용시 주의할 점

by 개발인생 2023. 6. 2.
반응형

배경

회사에서 개발 중 엑셀 업로드에 관한 기능을 구현할 때였다.

요구사항은 새로운 엑셀 파일 업로드시 기존에 저장된 내용을 전부 삭제하고 새로운 엑셀 파일의 데이터를 DB에 저장한다는 것이었다.

당시에는 아무런 생각없이 데이터 전체삭제를 deleteAll() 을 사용했다.

문제는 얼마 뒤 발생했는데 엑셀 업로드시 처리하는 시간이 오래걸려 오류가 발생했다.

이런 현상에는 더 많은 복합적인 이유가 있지만 그 중 이번 포스팅에서는 deleteAll() 사용에 대해 알아보려한다.

왜 느려지는가?

deleteAll() 이라는 이름만 놓고보면 전체를 삭제해줄 것 같다.

하지만 내부적으로는 전혀 다르게 진행되는 걸 확인할 수 있다.

     /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.Repository#deleteAll()
     */
    @Override
    @Transactional
    public void deleteAll() {

        for (T element : findAll()) {
            // 전체 엔티티를 하나씩 삭제한다.
            delete(element);
        }
    }

내부적으로는 deleteAll() 호출 시에 전체 엔티티를 조회한 뒤 하나씩 삭제를 하고있다.

예를들어 약 10000개의 데이터를 deleteAll() 한다면 10000개의 데이터를 하나씩 삭제를 한다는 뜻이다.

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
     */
    @Override
    @Transactional
    @SuppressWarnings("unchecked")
    public void delete(T entity) {

        Assert.notNull(entity, "Entity must not be null!");

        if (entityInformation.isNew(entity)) {
            return;
        }

        Class<?> type = ProxyUtils.getUserClass(entity);

        // 엔티티를 삭제하기 위해 해당 엔티티를 조회한다...

        T existing = (T) em.find(type, entityInformation.getId(entity));

        // if the entity to be deleted doesn't exist, delete is a NOOP
        if (existing == null) {
            return;
        }

        em.remove(em.contains(entity) ? entity : em.merge(entity));
    }

심지어 delete() 내부에서는 전달받은 엔티티를 한번 조회한 후 삭제 를 진행한다..

이로인해서 성능 이슈가 발생한 것이다.

해결 방법

이를 해결하기 위해서는 deleteAllInBatch() 메서드를 사용하는 방법과 직접 쿼리를 정의해 삭제하는 방법이 있다.

여기서는 deleteAllInBatch() 메서드에 대해서 알아보도록 한다.

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch()
     */
    @Override
    @Transactional
    public void deleteAllInBatch() {
        em.createQuery(getDeleteAllQueryString()).executeUpdate();
    }

deleteAllInBatch() 메서드의 코드이다.

엔티티 매니저가 삭제 쿼리를 생성해 사용하는 로직이 구현되어있다.

때문에 일일히 엔티티를 검색해 삭제하는 deleteAll() 보다 훨씬 효율이 높다.

깨달은 점

사실 이런 문제는 이슈가 발생하기 전에 미리 고려할 수 있었다.
JPA를 사용하면서 발생하는 쿼리를 유심히 살펴보는 습관이 많이 부족하다는 걸 깨달았다.
좀 더 꼼꼼히 성능이나 발생할 오류등에 대해 고려하는 능력을 길러야겠다.

반응형

댓글