JPA의 flush()는 언제, 왜 호출될까?
JPA의 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 과정이다. 즉, 영속성 컨텍스트의 변경 내용을 실제 DB에 반영하는 행위이다.
주의: flush()는 영속성 컨텍스트를 비우지 않는다. 단지, 쓰기 지연 SQL 저장소에 쌓여 있던 쿼리들을 데이터베이스에 전송할 뿐!
언제 자동으로 flush()가 호출될까?
기본적으로 flush()는 다음 시점에 자동 호출된다.
1. 트랜잭션이 커밋될 때
2. JPQL 쿼리를 실행할 때
1️⃣ 왜 JPQL 실행 시 자동으로 flush()가 일어날까?
예를 들어 하나의 트랜잭션 안에서 다음과 같은 코드가 있다면
em.persist(memberA); // 아직 DB에는 반영되지 않음
List<Member> result = em.createQuery("select m from Member m", Member.class).getResultList();
위와 같이 memberA를 persist() 한 뒤 바로 JPQL로 모든 Member를 조회한다면,
flush()를 하지 않으면 방금 등록한 memberA는 DB에 반영되지 않아 조회되지 않는다.
따라서 JPA는 JPQL 실행 전에 자동으로 flush()를 호출하여 동기화를 보장한다.
2️⃣ 왜 트랜잭션 커밋 시점에 자동으로 flush()가 일어날까?
그 이유는 다음과 같은 여러 가지 이점을 제공하기 때문이다.
1. 성능 최적화
여러 번의 insert/update 쿼리를 한 번에 모아서 전송(batch insert/update) 하므로, 불필요한 DB I/O를 줄일 수 있다.
2. 불필요한 쿼리 방지
변경 사항이 한 번에 모이기 때문에, 중간의 무의미한 update 쿼리를 줄일 수 있다.
3. 엔티티 상태 추적의 효율성
- JPA는 1차 캐시(Persistence Context) 를 통해 엔티티의 상태를 추적한다.
- 변경 사항이 감지되면 Dirty Checking(변경 감지) 을 통해 최소한의 쿼리만 전송한다.
4. 트랜잭션 일관성 유지
- 트랜잭션 단위로 작업이 처리되기 때문에, 중간에 오류가 나면 DB에 반영되지 않는다.
- 즉, 원자성(Atomicity) 보장에 유리하며, 불필요한 DB I/O도 줄일 수 있다.
💬 질문
그렇다면 일반적인 시나리오에서 flush()는 트랜잭션이 commit될 때 호출되기 때문에 원자성이 보장되는데, 중간에 JPQL 쿼리나 flush()를 직접 호출해서 데이터베이스에 쿼리가 반영되었지만 바로 다음 줄에 예외가 발생하면, 이미 나간 쿼리는 어떻게 롤백할 수 있을까?
✅ 답변
flush()는 단지 쿼리를 DB에 보내는 동작일 뿐,
트랜잭션이 커밋되는 시점까지는 실제로 확정되지 않는다.
따라서 실제 원자성은 트랜잭션의 커밋/롤백 메커니즘이 책임진다.
예시 코드)
em.persist(a);
em.flush(); // insert 쿼리 DB에 전송
int x = 1 / 0; // 예외 발생
tx.rollback(); // 트랜잭션 롤백, insert도 롤백됨
👉 트랜잭션이 커밋되기 전까지는 DB에 insert가 확정되지 않기 때문에, flush로 전송된 쿼리도 롤백이 가능하다!
끝.