The Checklist: Hibernate Health Audit
The Checklist: Hibernate Health Audit
This is not a chapter. It is a checklist. Print it. Run through it on your codebase. Fix what hurts. Ignore what does not.
Configuration
-
ddl-autois set tovalidateornonein production -
spring.jpa.open-in-viewis set tofalse - JDBC batch size is configured (
hibernate.jdbc.batch_size: 25-50) -
hibernate.order_insertsandhibernate.order_updatesaretrue -
hibernate.query.in_clause_parameter_paddingistrue - Connection pool size follows Little’s Law, not a guessed number
-
hibernate.generate_statisticsisfalsein production (enable only for debugging) - Second-level cache is enabled only for entities with measured cache hit rates above 80%
- Query cache is disabled unless you have validated invalidation behavior
ID Generation
- No
GenerationType.IDENTITYon batch-inserted entities (blocks JDBC batching) -
GenerationType.SEQUENCEusesallocationSize > 1(default 50 is good) - No
GenerationType.TABLEanywhere (row-level locking on the sequence table)
Fetch Strategies
- Default fetch type for
@OneToManyand@ManyToManyisLAZY(this is the JPA default, but verify) - Default fetch type for
@ManyToOneand@OneToOneisLAZY(JPA defaults to EAGER for these, override explicitly) - No
FetchType.EAGERon any association unless there is a measured reason -
@BatchSizeis set on frequently loaded lazy collections to prevent N+1 - Fetch joins (
JOIN FETCH) are used for known access patterns instead of relying on lazy loading
Queries
- Repository methods for list/search endpoints return DTOs, not entities
- No entity loading followed by mapping to DTOs (projection instead)
- Aggregate queries (SUM, COUNT, AVG) run in the database, not in Java
- No
Hibernate.initialize()outside of test code (use fetch joins or entity graphs instead) - All read-only service methods are annotated with
@Transactional(readOnly = true)
N+1 Detection
- SQL logging is enabled in development (
hibernate.show_sqlor p6spy) - A test exists that asserts query count for critical endpoints (use datasource-proxy or Hibernate statistics)
- No lazy collection access during JSON serialization (disabled OSIV should catch this)
Transactions
- No
@Transactionalmethods that call external HTTP services while holding a connection - Transaction boundaries are as narrow as possible (database work only)
-
@Transactional(readOnly = true)is used on all read-only paths - No
@Transactionalon controller methods (transactions belong in the service layer) - Optimistic locking retries use jittered exponential backoff, not immediate retry
Bulk Operations
- Bulk inserts flush and clear the persistence context every N rows (batch size)
- Bulk updates use JPQL
UPDATEstatements, not entity-by-entity modification - Bulk deletes use JPQL
DELETEstatements, notCascadeType.REMOVEon parent entities - Data imports of 10,000+ rows use
StatelessSessionor JDBC batch, not managed entities
Schema
- All schema changes go through Flyway or Liquibase
- No ghost columns in production (columns that exist in the database but not in entities)
- Indexes exist for all foreign keys and frequently queried columns
- Large table ALTER TABLE operations use
CONCURRENTLY(PostgreSQL) or online DDL (MySQL)
Monitoring
- Slow query log is enabled at the database level (not just Hibernate logging)
- Connection pool metrics are exposed (HikariCP metrics via Micrometer)
- Persistence context size is monitored for endpoints that load many entities
- GC pause metrics are correlated with Hibernate-heavy endpoints
When to Skip Hibernate Entirely
Check these boxes if they apply. Three or more checked means you should consider jOOQ, JDBC Template, or another alternative for that code path.
- The endpoint only reads data and never modifies it
- The query requires window functions, CTEs, or recursive queries
- The result set is larger than 10,000 rows
- The endpoint is latency-sensitive (p99 < 50ms)
- The code loads entities only to map them to DTOs immediately
- The operation is a bulk import, export, or batch update
- The team spends more time debugging Hibernate behavior than writing the query
This checklist covers the topics in this book. Not every item applies to every codebase. Start with the items that match symptoms you have already seen in production. Fix those first. Come back to the rest when you have time or when a new symptom appears.
The point of this book, and this checklist, is not to abandon Hibernate. Hibernate is a powerful tool for the problems it was designed to solve. The point is to recognize when you are outside those problems and to reach for the right tool instead of forcing the wrong one.