@SpringBootTest
class CustomerRepositoryTest extends Specification {
@Autowired
private CustomerRepository customerRepository;
def "Customer in 삭제" () {
given:
for(int i=0;i<100;i++){
customerRepository.save(new Customer(i+"님"))
}
when:
customerRepository.deleteByIdIn(Arrays.asList(1L,2L,3L))
then:
println "======= Then ======="
customerRepository.findAll().size() == 97
}
}
이 테스트로 확인을 해보면
in
쿼리로 조회하는 쿼리가 처음 실행된다.JpaQueryException 클래스를 확인해서 delete로 찾아보면
jpaQuery.reateQuery(value)
: select ~~ from Customer where ~~
이 생성된다.
delete만 했는데 왜 select해??
단건 뿐 아니라 여러건을 삭제하더라도 먼저 조회를 하고 얻은 결과로 엔티티 데이터를 1건씩 삭제한다.
50만건 삭제를 한다고 하면 먼저 조회하고 단건씩 삭제한다.
RepositoryQuery-> AbstractJpaQuery 에 JpaQueryExcution 타입을 가진 DeleteExcution을 보면
JpaRepository에서 delete를 실행할때 jpaQuery에서 createQuery를 호출한다는 사실을 알 수 있습니다.
cacade.DELETE 가 설정된 경우
in
쿼리로 조회하는 쿼리가 처음 실행됩니다.A
Id별로 B
을 조회한다.B
을 1건씩 삭제한다.A
을 1건씩 삭제한다.이 과정은 연관관계가 없는 DB 삭제와 달리 B 조회는 왜 발생했는지는 em.remove
를 봐야합니다.
DefaultDeleteEventListener
의 deleteEntity
메소드 를 살펴보면
cacadeBeforeDelete
메소드를 다시 파고들어야함을 알수 있습니다.
그리고 그 안에선 다시 Cascade.cascade 가 수행됩니다.
persister.getPropertyValue( parent, i )
메소드가 수행되면
~~~ from A A0 where A.B_id=?
이런 쿼리가 실행됩니다.
부모를 지울 때, 자식도 같이 지워야 하니까 자식 Key를 기준으로 모두 가져와서 하나씩 삭제하는 것이다.
그럼 cacade 옵션을 끄면 작동하나요? 바로 삭제 쿼리로 넘어가긴 하지만
이런 문제가 에러 즉, FK를 맺고 있는 테이블이 있어서 삭제가 안됩니다!
그러면 성능 이슈를 없애기 위해 방법은?
@Transactional
@Modifying
@Query("delete from Customer c where c.id in :ids")
void deleteAllByIdInQuery(@Param("ids") List<Long> ids);
이런식으로 작성해서 쿼리를 짜면 효과적입니다.
연관관계가 있는 경우에는 위와 같은 에러가 발생하므로
이렇게 둘다 지워주면 됩니다.