Audits

For a full description of what each audit detects, finding format, and exclusion types, see the core audit reference.

Catalog audits

Catalog audits read database metadata (information schema / system catalogs). No SQL execution is analyzed.

Audit Assertion Bean Exclusion type
PrimaryKeyPresenceAudit PrimaryKeyPresenceAuditAssertion Table names (Set<String>). Liquibase bookkeeping tables are excluded automatically.
PrimaryKeyTypeAudit PrimaryKeyTypeAuditAssertion Column names in table.column format (Set<String>)
DuplicateForeignKeyAudit DuplicateForeignKeyAuditAssertion FK constraint names (Set<String>)
ForeignKeyIndexAudit ForeignKeyIndexAuditAssertion FK constraint names (Set<String>)
ForeignKeyNotNullAudit ForeignKeyNotNullAuditAssertion Column names in table.column format (Set<String>)
ForeignKeyTypeMatchAudit ForeignKeyTypeMatchAuditAssertion Column names in table.column format (Set<String>)
RedundantIndexAudit RedundantIndexAuditAssertion Index names (Set<String>)
UniqueIndexNotNullAudit UniqueIndexNotNullAuditAssertion Index names (Set<String>)

Catalog audit example

@Autowired
private PrimaryKeyPresenceAuditAssertion primaryKeyPresenceAuditAssertion;

@Autowired
private ForeignKeyIndexAuditAssertion foreignKeyIndexAuditAssertion;

@Test
void assertPrimaryKeysPresent() {
    primaryKeyPresenceAuditAssertion.assertClean(schema);
}

@Test
void assertForeignKeysIndexed() {
    foreignKeyIndexAuditAssertion.assertClean(schema, Set.of("fk_legacy_unindexed_constraint"));
}

JPA audits

Audit Assertion Bean Exclusion type
SchemaEntityValidationAudit SchemaEntityValidationAuditAssertion None. The audit walks Hibernate’s entity mappings against the live schema and reports every mismatch (missing table, missing column, incompatible column type) in one run. Run under the default ddl-auto=none; do not set ddl-auto=validate, whose fail-fast startup check aborts the context on the first mismatch.
UnmappedDatabaseObjectAudit UnmappedDatabaseObjectAuditAssertion Relations — a table or table.column name, optionally schema-qualified (Set<String>). The reverse of SchemaEntityValidationAudit: proves the schema holds only what is mapped. Exclude migration-tool bookkeeping tables (e.g. databasechangelog, databasechangeloglock).
MissingVersionAttributeAudit MissingVersionAttributeAuditAssertion Entities — fully-qualified name, simple name, or physical table name (Set<String>)
EagerCollectionFetchAudit EagerCollectionFetchAuditAssertion Collection roles, e.g. com.acme.Order.items (Set<String>)

JPA audit example

class SchemaEntityValidationAuditIT extends AbstractDatabaseAuditIT {

    @Autowired
    private SchemaEntityValidationAuditAssertion schemaEntityValidationAuditAssertion;

    @Test
    void assertJpaSchemaClean() {
        schemaEntityValidationAuditAssertion.assertClean();
    }
}

Runtime audits

Runtime audits intercept SQL statements via Hibernate’s StatementInspector. They split into two groups by analysis technique.

Plan-based audits (PostgreSQL 16+ only)

WhereClauseIndexAudit, OrderByIndexAudit, and JoinIndexAudit call EXPLAIN (GENERIC_PLAN, FORMAT JSON) on each captured statement — a PostgreSQL-specific feature, so these audits are PostgreSQL-only and throw UnsupportedOperationException on any other platform. They also require preferQueryMode=simple on the test datasource JDBC URL. See Usage — PostgreSQL JDBC requirement.

Audit Assertion Bean Exclusion types
WhereClauseIndexAudit WhereClauseIndexAuditAssertion Relation names (Set<String>), SQL fragments (List<String>)
OrderByIndexAudit OrderByIndexAuditAssertion Relation names (Set<String>), SQL fragments (List<String>)
JoinIndexAudit JoinIndexAuditAssertion Relation names (Set<String>), SQL fragments (List<String>)
UnusedIndexAudit UnusedIndexAuditAssertion Index names (Set<String>). Advisory and workload-dependent: the capture must hold a representative workload, and a generic plan can miss an index the planner would pick under production data. Always confirm against production pg_stat_user_indexes before dropping an index this reports.

Token-scan audits (all platforms)

These detect via a token scan of the captured SQL — no EXPLAIN needed — so they run on every supported database platform.

Audit Assertion Bean Exclusion types
UnconditionalMutationAudit UnconditionalMutationAuditAssertion Exact statement strings (Set<String>)
OffsetPaginationAudit OffsetPaginationAuditAssertion SQL fragments (List<String>)
RepeatedStatementAudit RepeatedStatementAuditAssertion A threshold (int, minimum 2) plus SQL fragments (List<String>). Counts accumulate for the capturer’s whole lifetime, so the threshold is a regression tripwire (a generous default like 50), not a precise N+1 count.

Runtime audit example

// Priming test: must run before runtime audit assertions.
@Order(Integer.MIN_VALUE)
class RepositoryWorkloadIT extends AbstractDatabaseAuditIT {

    @Autowired
    private ParentRepository parentRepository;

    @Test
    void primeWorkload() {
        parentRepository.findAll();
        parentRepository.findById(1L);
    }
}

// Runtime audit: runs after priming.
@Order(Integer.MAX_VALUE)
class WhereClauseIndexAuditIT extends AbstractDatabaseAuditIT {

    @Autowired
    private WhereClauseIndexAuditAssertion whereClauseIndexAuditAssertion;

    @Test
    void assertWhereClauseIndexClean() {
        whereClauseIndexAuditAssertion.assertClean();
    }
}

Note: @Order across test classes requires ClassOrderer$OrderAnnotation enabled in junit-platform.properties. See Usage — SQL capture ordering.

Assertion outcomes

A finding is surfaced as an AssertionError (test failure), not an exception, so it appears as a discrete failing assertion in your test report with the audit’s curated fix-oriented message. An IllegalStateException (test error) indicates a configuration problem: an unsupported platform, an empty SQL capture (vacuous run), or a missing preferQueryMode=simple on PostgreSQL (see Usage — PostgreSQL JDBC requirement).