💻 Backend
JPA 와 QueryDSL 삭제(delete) 동작의 차이
date
‣
slug
jpa-querydsl-deleteall
author
status
Public
tags
SpringBoot
Java
summary
JPA 그리고 QueryDSL 사용 시 delete, deleteAll 의 동작 차이점
type
Post
thumbnail
category
💻 Backend
updatedAt
Jul 14, 2025 12:23 AM
JPA 와 QueryDSL 삭제(delete) 동작의 차이점주요 차이점1. JpaRepository.deleteAll()2. QueryDSL의 delete() 또는 deleteAll캐시 동기화 필요성캐시 동기화 필요성이 왜 차이가 날까?추가로 이해하기한번 더 질문하기요약 내용참고링크Comment
JPA 와 QueryDSL 삭제(delete) 동작의 차이점
주요 차이점
JPA와 QueryDSL 모두 deleteAll 기능을 제공하지만, 동작 방식과 1차 캐시(영속성 컨텍스트)에 미치는 영향에서 중요한 차이가 있다.
1. JpaRepository.deleteAll()
- 내부적으로 루프를 돌며 각 엔티티에 대해 em.remove() 호출
- 즉시 DB에 반영되지는 않지만, 1차 캐시에서 해당 엔티티는 제거
- em.remove()는 해당 엔티티를 삭제 상태로 마킹하며, 실제 DELETE SQL은 flush 또는 commit 시점에 실행
- 트랜잭션 커밋 또는 em.flush() 시점에 실제 SQL DELETE 실행
@Transactional public void deleteMembersAndSave() { memberRepository.deleteAll(members); // em.remove 반복: 1차 캐시내에서도 즉시 제거 memberRepository.saveAll(newMembers); // 안전하게 수행 가능 }
2. QueryDSL의 delete() 또는 deleteAll
- 내부적으로 JPQL DML (delete from Entity e where ...)을 생성
- EntityManager.createQuery(...).executeUpdate() 로 실행됨
- 대상 엔티티를 영속성 컨텍스트에 올리지 않고 직접 SQL로 삭제
- 1차 캐시에 이미 존재하는 동일 엔티티와 불일치할 수 있음
queryFactory .delete(member) .where(member.age.gt(30)) .execute(); entityManager.clear(); // 반드시 캐시 초기화 필요
캐시 동기화 필요성
방식 | 1차 캐시 동기화 여부 | 추가 작업 필요 여부 |
JPA deleteAll() | ✅ 자동 반영 | ❌ 없음 |
QueryDSL delete() | ❌ 반영 안 됨 | ✅ em.clear() 필요 |
캐시 동기화 필요성이 왜 차이가 날까?
QueryDSL은 단순히 “빌더 API”이다.
JPA 기반일 경우 내부적으로 JPA가 이해할 수 있는 JPQL을 타입 안전하게 생성하는 빌더 역할을 함 즉, JPA 쿼리를 “직접” 사용하는 건 아니지만 JPQL을 타입 안전하게 “생성해서 JPA가 실행”하도록 위임하는 방식임
- 내부적으로 JPQL 생성하고, 이 JPQL은 JPA의 EntityManager를 통해 실행됨
select m.name from Member m where m.age > 20
- 실행 순서
- QueryDSL: 타입 안전한 방식으로 JPQL 문자열을 생성
- EntityManager.createQuery(…) 호출
- JPA 구현체(Hibernate 등):
- JPQL 파싱
- 적절한 SQL로 변환
- 영속성 컨텍스트 사용 여부 결정
- SQL 실행 (JDBC):
- DB에 SQL 쿼리 전송
- 결과를 JDBC ResultSet으로 수신
- 결과 매핑:
- SQL 결과 → 엔티티(
Member
)로 매핑 - 필요시 영속성 컨텍스트(1차 캐시)에 등록
List<Member> result = queryFactory .selectFrom(member) .where(member.age.gt(30)) .fetch();
select m from Member m where m.age > 30
entityManager.createQuery("select m from Member m where m.age > 30", Member.class);
SELECT m.id, m.name, m.age FROM member m WHERE m.age > 30
⚠️ 단, bulk delete/update(JPQL DML)는 엔티티를 영속성 컨텍스트에 등록하지 않으며
1차 캐시와의 동기화가 이루어지지 않으므로 이후 작업에서는 직접 clear() 또는 flush()를 고려해야 함
추가로 이해하기
한번 더 질문하기
Q. QueryDSL은 내부적으로 JPA 쿼리를 쓰는가?
A. JPA의 CriteriaQuery를 쓰는 건 아님
JPA가 이해할 수 있는 JPQL 문자열을 타입 안전하게 생성해서 넘기고
최종적으로 실행하는 주체는 EntityManager(JPA) 는 맞음
요약 내용
- JPA 에서 delete 사용 시 즉시 1차 캐시에서도 삭제 마킹(em.remove)
- em.remove()는 객체 단위 삭제로, 1차 캐시에 있는 엔티티도 함께 제거됨
- QueryDSL 을 사용하는 경우 내부적으로 JPQL 을 생성하기에 1차 캐시 초기화는 별도로 작성해야함
- JPQL DML (delete/update)은 객체를 무시하고 SQL만 날림 캐시 무시
- QueryDSL은 JPA가 이해할 수 있는 JPQL을 타입 안전하게 생성하는 빌더 역할
- 실행 주체는 JPA의 EntityManager: JPQL을 SQL로 변환 후 JDBC로 실행
참고링크
Comment
JPA의 deleteAll()은 단건 삭제를 반복하기 때문에 성능상 이슈가 있을 수 있으나,
데이터 일관성과 영속성 컨텍스트의 동기화 측면에서는 매우 안전하다.
반면 QueryDSL의 bulk delete는 성능은 좋지만,
항상 flush()/clear()를 명시적으로 호출해주는 것이 안전하다.