Performance and Statistics
|
This section documents Hibernate ORM 7.4.x (User Guide, Introduction, Query Language Guide, Data Repositories Guide), Jakarta Persistence 3.2, Hibernate Search 8.4.x, and the Hibernate Validator / Hibernate Reactive references — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against those official docs before being relied on in production. Three older reference books were consulted as bibliography only while preparing these pages and are not the
primary or main source for any page. All three predate Jakarta Persistence 3.2 and Hibernate ORM 6/7 (the
This section’s bibliography lists the reference material consulted while preparing these pages. |
Most of Hibernate’s performance surface is already covered per-topic elsewhere in this section (Fetching & N+1, Bulk Operations & Batching, Second-Level Cache). This page is the checklist and the diagnostic tools that tell you which of those actually matters for a given application.
The performance checklist, distilled
-
Default every association to
FetchType.LAZY; load eagerly per-query viaJOIN FETCH/entity graphs only for what a specific use case needs. -
Set
spring.jpa.open-in-view=false(or the equivalent for a non-Spring bootstrap) and fetch exactly what a response needs inside the transactional service method. -
Use
SEQUENCE(notIDENTITY) generation where the database supports it, to keep JDBC batching intact. -
Set
hibernate.jdbc.batch_sizeandhibernate.order_inserts/order_updatesfor any workload doing more than a handful of writes per transaction. -
Project read-only views as DTOs (
SELECT new) instead of loading full managed entities. -
Reach for
StatelessSessionor theflush()/clear()loop for large batch/ETL work. -
Cache only what is actually re-read often and rarely changes; verify with the
StatisticsAPI rather than assuming a cache region is paying for itself (see Second-Level Cache's query-cache pitfall).
The Statistics API and hibernate.generate_statistics
Statistics stats = sessionFactory.getStatistics();
stats.setStatisticsEnabled(true); // also settable via hibernate.generate_statistics=true
long queries = stats.getQueryExecutionCount();
long slowest = stats.getQueryExecutionMaxTime();
String slowestQuery = stats.getQueryExecutionMaxTimeQueryString();
CacheRegionStatistics bookCache = stats.getCacheRegionStatistics("book");
double hitRatio = (double) bookCache.getHitCount()
/ (bookCache.getHitCount() + bookCache.getMissCount());
Statistics exposes per-entity, per-collection, per-query, and per-cache-region counters (load/fetch/update/
insert/delete counts, cache hit/miss/put counts, query execution counts and timings) — the direct way to
confirm a suspected N+1 pattern, a cold cache region, or a slow query, rather than guessing from application
behavior alone.
The slow-query log
hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS logs (at WARN) any single query whose execution
exceeds the given threshold, including the SQL and its execution time — a low-overhead, always-on way to catch
performance regressions in production without enabling full statistics collection or hibernate.show_sql.
Java Flight Recorder events
Hibernate ORM emits JFR (Java Flight Recorder) events for key persistence-context operations (session opens, flushes, JDBC batch execution, cache access) when running under a JDK with JFR enabled — lets Hibernate-level activity be correlated, in one recording, with the rest of the JVM’s own GC/allocation/lock-contention events already captured by JFR, without adding a separate Hibernate-specific profiling agent.
Connection-pool tuning
Hibernate’s own performance is frequently bottlenecked by the connection pool underneath it rather than
anything Hibernate itself does: a pool sized too small serializes otherwise-parallel requests waiting for a
connection; a pool sized too large can overwhelm the database’s own max-connections limit. HikariCP (the
Spring Boot default) exposes maximum-pool-size, minimum-idle, connection-timeout, and leak-detection-
threshold as the settings worth tuning first; see also
Metrics & Observability for exposing pool metrics via
Micrometer/Actuator.
Common anti-patterns
-
Open-session-in-view left on by default — hides N+1 problems in the serialization layer and holds a database connection for the whole request; see Fetching & N+1.
-
Eager fetching everywhere — loading associations that most call sites never use, on every load of the owning entity.
-
Entity-instead-of-DTO for read-only views — pays persistence-context and dirty-checking overhead for data that is never going to be written back.
-
@OneToManywith no@BatchSize— lazily-initialized collections default to oneSELECTper parent, the textbook N+1 shape, unless batched.