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

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

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

A persistence-context cache can make a repository test pass without proving that the database contains the expected row.

Test the stored state

A repository test saves a receipt, then calls findById in the same persistence context. JPA may hand back the already managed object. That proves little about column mapping, database defaults, or a query predicate. Flush pending SQL, clear the persistence context, then read again. A flush can expose a constraint failure, while the clear forces a later lookup to materialize data from the database. Flush remains different from commit].

Choose the assertion boundary

For a derived tenant query, persist one local receipt and one foreign-tenant receipt, flush and clear, then assert only the local row appears. A unit mock cannot validate JPQL or the actual tenant predicate. For a test of joined page totals], seed multiple matching child rows and assert both content and totalElements after clearing. Keep the dataset small enough to diagnose a failure, but varied enough to expose row multiplication.

Account for rollback

@DataJpaTest normally wraps each test in a transaction and rolls it back. That isolates many repository tests, but it does not prove a commit-time constraint, an after-commit listener, or what another transaction sees. Use a separate committed-boundary test for those contracts. If you inspect SQL counts, reset the counter after setup; fixture inserts otherwise contaminate the measurement.

Implementation contract

Java
@DataJpaTest
class ReceiptTenantQueryTest {
    @Autowired ReceiptRepository receipts;
    @Autowired EntityManager entityManager;

    @Test void excludesOtherTenantAfterDatabaseRoundTrip() {
        receipts.save(ReceiptEntity.forTenant(NORTH_TENANT, "R-47"));
        receipts.save(ReceiptEntity.forTenant(SOUTH_TENANT, "R-47"));
        entityManager.flush();
        entityManager.clear();

        assertThat(receipts.findByTenantId(NORTH_TENANT))
            .extracting(ReceiptEntity::getTenantId)
            .containsExactly(NORTH_TENANT);
    }
}

Cost and verification

Flush adds a database synchronization point; clear discards managed objects and requires another read. That extra work is useful in persistence tests but would be needless ceremony in a plain unit test with no JPA context.

Common Mistakes

  • Do not assert against the same managed entity and call it a database round trip.
  • Do not treat a rolled-back slice test as proof of commit-time behavior.
  • Do not include setup statements in the query budget being measured.

Read next

Spring Data JPA saveAndFlush is a SQL boundary, not a commit, Spring Data JPA count query for a joined Page, Spring Data JPA Specifications with a mandatory tenant predicate, Spring Data JPA test against the deployment database dialect, Spring command rollback test: inspect state after an injected failure.

spring
spring-boot
testing
datajpa-test-flush-clear
Storage details