티스토리 뷰

스프링부트에서 JPA를 사용하다가 모든 리스트를 다 지우고 처음으로 초기화 할 일이 생겼다.

그래서 나는 repository의 deleteAll() 함수를 사용한 후 for문을 통해 처음에 집어넣었던 값들을 똑같이 DB에 집어넣고자 saveAllAndFlush()를 했다. 그렇게 하면 기존에 이것저것 수정된 DB 데이터들이 모두 삭제되고 처음에 집어넣었던 값들을 다시 DB에 넣어서 DB는 이것저것 사용하기 전의 처음 값들을 가지게 될 것이라고 생각한 것이다.

하지만 primary key인 id 값이 이미 존재한다며 'javax.persistence.EntityExistsException' 에러가 발생했고 혹시 deleteAll()을 하고 나서 flush를 안 해서 그런가 하고 repository의 flush()를 사용해봤다. 하지만 결과는 똑같았다.

왜 일까?

 

 

JPA deleteAll()후 save()를 하면 EntityExistsException 에러가 발생하는 이유

JAP repository에서 지원하는 deleteAll() 함수 내부를 살펴보면 아래와 같이 되어 있다.

/*
 * (non-Javadoc)
 * @see org.springframework.data.repository.Repository#deleteAll()
 */
@Override
@Transactional
public void deleteAll() {
	for (T element : findAll()) {
		delete(element);
	}
}

 

안에서 findAll()로 모든 데이터를 찾아서 for iterator를 통해 해당 데이터를 하나하나 삭제를 하는 로직으로 돌아가고 있다.

실제로 deleteAll()를 사용해보면 쿼리가 아래와 같이 날아간다.

 

바로 여기서 문제가 생기는데 위와 같이 iterator를 이용한 for문은 비동기로 처리가 된다. 즉, findAll()로 찾은 데이터를 모두 delete하기도 전에 다음 로직으로 넘어간다는 것이다.

그러니 deleteAll() 다음에 똑같은 데이터를 만들어서 save하는 것에서 미처 삭제되지 않은 것이 있어 EntityExistsException 에러가 발생하는 것이다.

 

 

JPA deleteAll()후 save()를 하면 EntityExistsException 에러 해결 방법

그래서 어떻게 해야하나, 따로 index를 이용한 for문을 만들어서 하나 하나 데이터를 지워줘야 하나? 라고 생각하며 JPA에서 기본으로 제공하는 함수로 해결할 방법은 없을까 찾아봤다.

그리고 의외로 쉽게 문제를 해결했다. 바로 deleteAll() 대신 deleteAllInBatch()를 사용하는 것이다.

deleteAllInBatch() 함수 내부를 보면 아래와 같이 간단하게 구현되어 있다.

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

 

내부를 보면 deleteAll()과 다르게 createQuery를 이용해서 query를 생성하고 실행시킨다는 것을 알 수 있다. 즉, for문으로 데이터를 하나 하나 지우는 것이 아닌, getDeleteAllQueryString()으로 모든 데이터를 제거하는 쿼리를 생성해서 한 번에 다 지운다는 것이다.

 

getDeleteAllQueryString()은 아래와 같이 DELETE_ALL_QUERY_STRING이라는 문자와 해당 entity의 이름(테이블 이름)이 넣어서 query를 생성하도록 되어 있다.

private String getDeleteAllQueryString() {
	return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());
}

 

DELETE_ALL_QUERY_STRING은 아래와 같이 우리가 흔히 하는 'delete from' 문으로 되어 있다. 즉, deleteAllInBatch()에서는 그냥 조건 없이 'delete from ${테이블}'하는 쿼리를 생성해 바로 실행시킨다는 것이다.

public static final String DELETE_ALL_QUERY_STRING = "delete from %s x";

 

실제 쿼리도 확인해보면 아래와 같이 그냥 해당 테이블에 있는 모든 데이터를 삭제하는 쿼리를 날리고 있다.

 

즉, 모든 데이터를 삭제하고 바로 DB에 무언가 insert를 할거면 deleteAllInBatch()를 사용해야 한다. deleteAll()은 또한 delete 문을 여러 개 날려서 데이터를 하나 하나 삭제하는 것이므로 많은 데이터를 다 삭제할거라면 그 때도 deleteAllInBatch()를 사용하길 추천한다.

 

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함