Study/SpringBoot

JPA의 flush()는 언제, 왜 호출될까?

kanado 2025. 4. 12. 20:29

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로 전송된 쿼리도 롤백이 가능하다!

 

끝.