2025-07-02
save() vs saveAndFlush() 차이점
항목 | save() | saveAndFlush() |
기능 | 엔티티를 영속성 컨텍스트(Persistence Context)에 저장 | 엔티티 저장 + flush()를 즉시 호출 |
DB 반영 시점 | flush 시점 (트랜잭션 커밋 or 명시적 flush 시) | 즉시 flush → DB에 SQL 실행 |
트랜잭션 내에서 커밋 전까지 DB에서 조회 불가 | ✅ 가능 | ❌ flush로 DB에 즉시 반영됨 |
성능 | 일반적으로 더 효율적 (버퍼링) | 성능 비용 있음 (즉시 DB IO) |
주 용도 | 일반적인 CRUD 처리 | DB에 즉시 반영해야 할 때 (예: ID, 제약조건 확인 등) |
왜 둘 다 존재하는가?
- save()는 일반적인 "지연 쓰기" 전략에 최적화되어 있어 성능이 좋음.
- 하지만 실무에서는 다음과 같은 즉시 DB 반영이 필요한 시점이 존재함:
📌 대표적인 saveAndFlush() 사용 케이스
- `saveAndFlush()`는 동일 트랜잭션 내에서 JPA 쿼리로 곧바로 조회해야 할 때 사용합니다.
- 이 경우 flush가 되지 않으면 영속성 컨텍스트에만 존재하므로 JPQL 등에서 조회 불가한 경우가 있음.
- 다만, 다른 커넥션 또는 트랜잭션에서 직접 DB에 접근해도 보이지 않으며, 트랜잭션이 commit 되어야만 외부에서 확인할 수 있습니다.
flush() vs commit
- flush() = 영속성 컨텍스트의 변경 내용을 SQL로 변환하여 DB에 반영
- commit = 트랜잭션 종료 및 확정
flush() ⇒ SQL 실행 (하지만 트랜잭션은 열려 있음) commit ⇒ 트랜잭션 종료 (이때 자동으로 flush 발생)
@Transactional 안에서 saveAndFlush()
@Transactional
public void saveAndImmediatelyCheck() {
MyEntity saved = repository.saveAndFlush(myEntity);
// 여기서 DB에 SQL이 실제 실행됨 → DB에 물리적으로 반영되어 있음
// 이후 Native Query 또는 다른 Connection으로도 조회 가능
}
- ✅ YES, saveAndFlush()는 @Transactional 내부에서도 DB에 즉시 반영됩니다.
- 단, 트랜잭션이 rollback되면 전체 변경사항은 되돌아감 (flush는 SQL을 날릴 뿐, 트랜잭션은 살아있음)
주의 사항
@Transactional 메서드 내부에서 saveAndFlush() 실행
- 트랜잭션은 아직 commit 되지 않은 상태
- 이 상태에서 DB에 직접 쿼리(다른 커넥션으로) 날려서 해당 데이터를 조회할 경우
결과는?
조회할 수 없습니다. (변경 사항이 보이지 않음)
즉, saveAndFlush()로 DB에 SQL이 날아가긴 하지만, 같은 트랜잭션 내에서만 보일 뿐 외부에서는 보이지 않습니다.
이유: 트랜잭션 격리 수준 (Isolation Level)
기본적으로 Spring + JPA + RDBMS (예: MySQL, PostgreSQL) 조합에서 사용하는 격리 수준은:
READ COMMITTED 또는 그 이상 (REPEATABLE READ, SERIALIZABLE)
이 말은 다음과 같습니다:
- 트랜잭션 내에서 flush() → SQL 실행은 되지만,
- 트랜잭션 commit 전까지는 다른 커넥션에서 해당 변경사항이 보이지 않음
- 즉, 자기 자신의 트랜잭션 범위 내에서만 확인 가능
정리된 흐름
구분 | 설명 |
save() | 영속성 컨텍스트에만 저장됨 (flush 안됨) |
saveAndFlush() | SQL이 DB로 전송되어 실행됨 (→ 하지만 트랜잭션이 열린 상태이므로 commit되지 않음) |
외부 DB 쿼리 | 다른 커넥션에서 조회 → 변경 내용 보이지 않음 |
트랜잭션 커밋 이후 | 외부에서도 조회 가능 |
실무에서 자주 오해하는 부분
- saveAndFlush()는 DB에 SQL을 날리긴 하지만, 트랜잭션이 끝나지 않았기 때문에 다른 커넥션에서는 변경 내용을 볼 수 없다는 점.
- 이건 ACID 중 ‘Isolation’ 속성 때문에 보장되는 동작입니다.
예시 (다른 커넥션으로 조회)
@Transactional
public void testFlushBehavior() {
MyEntity e = new MyEntity("abc");
repository.saveAndFlush(e); // INSERT 쿼리는 실행됨
// 다른 DataSource / JDBC 연결에서 동일 row를 SELECT 해도 → 조회 불가
// 현재 트랜잭션이 커밋되지 않았기 때문
}
실무 팁
- saveAndFlush()는 "바로 쿼리 날리고, 이후 로직에서 JPA로 바로 조회할 때" 유용함
- 외부 시스템에서 조회하거나 DB Trigger 테스트할 때는 commit()도 완료되어야 함
- DB에 눈으로 확인하려면 트랜잭션이 끝나야 한다
결론
질문 | 답변 |
saveAndFlush() 후 DB에 직접 쿼리하면 값 보이나? | ❌ 보이지 않음 |
이유는? | 트랜잭션이 아직 commit되지 않았기 때문. 트랜잭션 격리 수준에 따라 외부 커넥션에서는 변경 사항이 보이지 않음 |
언제 확인 가능? | @Transactional 메서드가 끝나서 커밋된 이후 |
메인 이미지 출처 : 사진: Unsplash의Marek Piwnicki