Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring Data JPA saveAndFlush is a SQL boundary, not a commit

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

Flush can surface a database constraint error before method return while the surrounding transaction may still roll back.

Distinguish three events

save places or merges entity state in the persistence context. Flush synchronizes pending changes with the database inside the current transaction. Commit makes the transaction durable and visible according to database isolation. Calling saveAndFlush can force a unique-key rejection earlier, but a later failure still rolls back the flushed row. Transaction ownership remains at the service boundary, not the repository call.

Do not publish on flush

An order acceptance service writes a receipt and an outbox row in one transaction. If it sends a broker message immediately after saveAndFlush, a later rollback can leave a message announcing a receipt that never committed. Persist an outbox obligation in the same database transaction and deliver it later through the relay boundary. A flush also does not make a concurrent transaction see an uncommitted row.

Exercise the rollback path

Save a receipt with a unique tenant and external key, flush it, then throw a test exception before method exit. Assert the row is absent in a new transaction. Repeat with a duplicate key to check when the exception appears for the chosen database and constraint mode. Some constraints are deferred until commit, so do not promise that every violation is caught by flush.

Implementation contract

Java
@Transactional
public UUID accept(ReceiptCommand command) {
    ReceiptEntity receipt = receipts.saveAndFlush(
        ReceiptEntity.from(command));
    outbox.save(OutboxEvent.receiptAccepted(receipt.getId()));
    return receipt.getId();
}

Cost and verification

An explicit flush adds a synchronization point and can reduce batching opportunities. Use it when an earlier database check or generated value is required, not after every save. The transaction still holds resources until commit or rollback.

Common Mistakes

  • Do not treat saveAndFlush as a durable commit.
  • Do not send an external message after flush and before commit.
  • Do not assume every database constraint is checked before commit.

Read next

Spring TransactionTemplate: roll back a failed multi-row change, Spring outbox transaction boundaries: where atomicity ends, Spring Data JPA bulk update: the managed entity can still hold the old value, Spring read-only transaction: performance hint, not a write guard, Spring JPA optimistic locking: reject a stale stock update.

Test this boundary

Spring Data JPA test: flush and clear before trusting a read

Related data contract

JPA flush mode: know when pending writes become SQL.

spring
spring-data-jpa
jpa-saveandflush-not-commit
Storage details