A beginner’s guide to Hibernate Statistics

Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

Hibernate provides a very powerful Statistics mechanism that, unfortunately, is lesser known. In this article, we are going to see how the Hibernate Statistics mechanism works, and how you can activate.

While the Hibernate Statistics mechanism is not enabled by default, you will see that many applications can benefit from using it, especially when it comes to determining cache effectiveness.

Statistics interface

The Hibernate Statistics class hierarchy looks as follows:

Hibernate Statistics class hierarchy

The Hibernate org.hibernate.stat.Statistics interface contains an exhaustive list of metrics you can inspect in regards to the behavior of the current Hibernate SessionFactory or JPA EntityManager.

General-purpose methods

The following Statistics methods are used to describe or control the current Hibernate Statistics object.

boolean isStatisticsEnabled()
It determines whether the Hibernate statistic mechanism is enabled or not.
void setStatisticsEnabled(boolean b)
It enables or disables the Hibernate Statistics mechanism.
void clear()
It removes the current collected metrics.
long getStartTime()
It gets the timestamp when the Statistics instance was instantiated.
void logSummary()
It prints a log entry containing the most important metrics.

EntityManager or Session metrics

The following metrics are associated with the current Persistence Context:

long getSessionOpenCount()
It provides the number of Session or EntityManager instances that were opened by the current EntityManagerFactory or SessionFactory.
long getSessionCloseCount()
It provides the number of Session or EntityManager instances that were closed by the current EntityManagerFactory or SessionFactory.
long getFlushCount()
It provides the number of times the Session or EntityManager instances were flushed.

Database metrics

The following metrics are associated with either the database connection, transaction or executing statements:

long getConnectCount()
It gives the number of database connections that were acquired by the current EntityManagerFactory or SessionFactory.
long getSuccessfulTransactionCount()
It gives the number of transactions that were committed by the current EntityManagerFactory or SessionFactory.
long getTransactionCount()
It gives the number of transactions that were executed by the current EntityManagerFactory or SessionFactory.
long getPrepareStatementCount()
It provides the number of JDBC PreparedStatements that were created by the current EntityManagerFactory or SessionFactory.
long getCloseStatementCount()
It provides the number of JDBC statement instances that were closed by the current EntityManagerFactory or SessionFactory.

Entity metrics

The following metrics are associated with the JPA or Hibernate entities:

String[] getEntityNames()
It provides the names of all entities that are registered with the current EntityManagerFactory or SessionFactory.
EntityStatistics getEntityStatistics(String entityName)
It provides you the EntityStatistics object for the provided entity name.
long getEntityLoadCount()
It gives you the total number of entities that were loaded (including the one fetched from the first or second-level cache or the database) by the current EntityManagerFactory or SessionFactory.
long getEntityFetchCount()
It gives you the total number of entities that were fetched from the database by the current EntityManagerFactory or SessionFactory.
long getEntityInsertCount()
It gives you the total number of entities that were inserted by the current EntityManagerFactory or SessionFactory.
long getEntityUpdateCount()
It gives you the total number of entities that were updated by the current EntityManagerFactory or SessionFactory.
long getEntityDeleteCount()
It gives you the total number of entities that were deleted by the current EntityManagerFactory or SessionFactory.

Collection metrics

The following metrics are associated with the JPA or Hibernate entity collections:

String[] getCollectionRoleNames()
It provides the names of all entity collections that are registered with the current EntityManagerFactory or SessionFactory.
CollectionStatistics getCollectionStatistics(String role)
It provides you the CollectionStatistics object for the provided entity collection name.
long getCollectionLoadCount()
It gives you the total number of collections that were loaded (including the one fetched from the first or second-level cache or the database) by the current EntityManagerFactory or SessionFactory.
long getCollectionFetchCount()
It gives you the total number of collections that were fetched from the database by the current EntityManagerFactory or SessionFactory.
long getCollectionUpdateCount()
It gives you the total number of collections that were updated by the current EntityManagerFactory or SessionFactory.
long getCollectionRemoveCount()
It gives you the total number of collections that were removed by the current EntityManagerFactory or SessionFactory.
long getCollectionRecreateCount()
It gives you the total number of collections that were recreated by the current EntityManagerFactory or SessionFactory.

Query metrics

The following metrics are associated with the queries that were executed by the current JPA EntityManagerFactory or Hibernate SessionFactory:

String[] getQueries()
It returns the entity queries (e.g. HQL and Criteria API) executed by the current JPA EntityManagerFactory or Hibernate SessionFactory.
QueryStatistics getQueryStatistics(String queryString)
It returns the QueryStatistics associated to the provided query.
long getQueryExecutionCount()
It gives the number of queries that were executed by the current JPA EntityManagerFactory or Hibernate SessionFactory.
long getQueryExecutionMaxTime()
It gives the maximum execution time of all queries that were executed.
String getQueryExecutionMaxTimeQueryString()
It gives the name of the query with the longest execution time.
long getQueryPlanCacheHitCount()
It provides the hit count for the Query Plan Cache.
long getQueryPlanCacheMissCount()
It provides the miss count for the Query Plan Cache.

Natural identifier metrics

The following metrics are associated with the Hibernate natural identifier mechanism:

NaturalIdStatistics getNaturalIdStatistics(String entityName)
It returns the NaturalIdStatistics associated with the provided entity name.
long getNaturalIdQueryExecutionCount()
It gives the number of times a given entity identifier was resolved by its associated natural id.
long getNaturalIdQueryExecutionMaxTime()
It gives the maximum execution time a given entity identifier was resolved by its associated natural id.
String getNaturalIdQueryExecutionMaxTimeRegion()
It gives the second-level cache region name with the longest natural id resolving query.
String getNaturalIdQueryExecutionMaxTimeEntity()
It gives the entity name for whichthe longest natural id resolving query was recorded.

Second-level cache metrics

The following metrics are associated with the second-level caching mechanism:

String[] getSecondLevelCacheRegionNames()
It provides the names of all regions used by the second-level cache.
CacheRegionStatistics getDomainDataRegionStatistics(String regionName)
It provides the CacheRegionStatistics associated with the given region name.
CacheRegionStatistics getQueryRegionStatistics(String regionName)
It provides the query CacheRegionStatistics associated with the given region name.
CacheRegionStatistics getCacheRegionStatistics(String regionName)
It provides the domain data or query CacheRegionStatistics associated with the given region name.
long getSecondLevelCacheHitCount()
It provides the global hit count of all entity or collection cache regions.
long getSecondLevelCacheMissCount()
It provides the global miss count of all entity or collection cache regions.
long getSecondLevelCachePutCount()
It provides the global put count of all entity or collection cache regions.
long getNaturalIdCacheHitCount()
It provides the global hit count of the natural id cache region.
long getNaturalIdCacheMissCount()
It provides the global miss count of the natural id cache region.
long getNaturalIdCachePutCount()
It provides the global put count of the natural id cache region.
long getQueryCacheHitCount()
It provides the hit count of the query cache region.
long getQueryCacheMissCount()
It provides the miss count of the query cache region.
long getQueryCachePutCount()
It provides the put count of the query cache region.
long getUpdateTimestampsCacheHitCount()
It provides the hit count of the timestamp cache region, which is used by the query cache.
long getUpdateTimestampsCacheMissCount()
It the miss count of the timestamp cache region, which is used by the query cache.
long getUpdateTimestampsCachePutCount()
It provides the global put count of the timestamp cache region, which is used by the query cache.

Concurrency-control metrics

long getOptimisticFailureCount()
It provides the number of optimistic locking failures detected by the current EntityManagerFactory or SessionFactory.

StatisticsImplementor interface

The org.hibernate.stat.spi.StatisticsImplementor interface extends the aforementioned Statistics interface and defines multiple callback methods that are executed by the Hibernate core API.

EntityManager or Session metrics

void openSession()
This callback is called when a JPA EntityManager or the Hibernate Session is created.
void closeSession()
This callback is called when a JPA EntityManager or the Hibernate Session is closed.
void flush()
This callback is called when a JPA EntityManager or the Hibernate Session is flushed.

Database metrics

void connect()
This callback is called when a database connection is acquired from the currently configured ConnectionProvider.
void prepareStatement()
This callback is called when a JDBC PreparedStatement instance is created.
void closeStatement()
This callback is called when a JDBC PreparedStatement instance is closed.
void endTransaction(boolean success)
This callback is called when the RESOURCE_LOCAL or JTA transaction is either committed or rolled back.

Entity metrics

void loadEntity(String entityName)
This callback is called when a given entity is loaded either as a Proxy or by fetching it from the database.
void fetchEntity(String entityName)
This callback is called when a given entity is fetched from the database.
void insertEntity(String entityName)
This callback is called when a given entity is inserted.
void updateEntity(String entityName)
This callback is called when a given entity is updated.
void deleteEntity(String entityName)
This callback is called when a given entity is deleted.

Collection metrics

void loadCollection(String role)
This callback is executed when a given entity collection is either created or loaded.
void fetchCollection(String role)
This callback is executed when a given entity collection is fetched from the database.
void updateCollection(String role)
This callback is executed when a given entity collection is updated.
void recreateCollection(String role)
This callback is executed when a given entity collection is recreated.
void removeCollection(String role)
This callback is executed when a given entity collection is removed.

Query metrics

void queryExecuted(String hql, int rows, long time)
This callback is called after an entity or native SQL query is executed.
void queryPlanCacheHit(String hql)
This callback is called when the entity query plan was loaded from the query plan cache.
void queryCompiled(String hql, long microseconds)
This callback is called when the entity query plan could not be loaded from the query plan cache.

Second-level cache metrics

void entityCachePut(NavigableRole entityName, String regionName)
This callback is executed when a given entity is added to the second-level cached.
void entityCacheHit(NavigableRole entityName, String regionName)
This callback is executed when a given entity is loaded from the second-level cached.
void entityCacheMiss(NavigableRole entityName, String regionName)
This callback is executed when a given entity was not found in the second-level cached.
void collectionCachePut(NavigableRole collectionRole, String regionName)
This callback is executed when a given entity collection was added to the second-level cached.
void collectionCacheHit(NavigableRole collectionRole, String regionName)
This callback is executed when a given entity collection is loaded from the second-level cached.
void collectionCacheMiss(NavigableRole collectionRole, String regionName)
This callback is executed when a given entity collection was not found in the second-level cached.
void naturalIdCachePut(NavigableRole rootEntityName, String regionName)
This callback is executed when a given natural id is added to the second-level cached.
void naturalIdCacheHit(NavigableRole rootEntityName, String regionName)
This callback is executed when a given entity identifier is resolved via its associated natural id from the second-level cached.
void naturalIdCacheMiss(NavigableRole rootEntityName, String regionName)
This callback is executed when a given entity identifier could not be resolved via its associated natural id from the second-level cached.
void naturalIdQueryExecuted(String rootEntityName, long executionTime)
This callback is called after the entity identifier query by the associated natural id is executed.
void queryCachePut(String hql, String regionName)
This callback is called when a query cache entry is stored in the second-level query cache region.
void queryCacheHit(String hql, String regionName)
This callback is called when a query cache entry is loaded from the second-level query cache region.
void queryCacheMiss(String hql, String regionName)
This callback is called when a query cache entry could not be loaded from the second-level query cache region.
void updateTimestampsCacheHit()
This callback is called when the tablespace last update timestamp is loaded from the timestamp query cache region
void updateTimestampsCacheMiss()
This callback is called when the tablespace last update timestamp could not be loaded from the timestamp query cache region
void updateTimestampsCachePut()
This callback is called when the tablespace last update timestamp is added to the timestamp query cache region

Concurrency-control metrics

void optimisticFailure(String entityName)
This callback is called when an optimistic locking exception is thrown.

These callbacks are very useful if you want to customize the Hibernate Statistics mechanism.

Activating the statistics mechanism

By default, the statistic mechanism is disabled. To enable it, you need to set the following configuration property:

<property name="hibernate.generate_statistics" value="true"/>

Once activated, the metrics are stored in the org.hibernate.stat.internal.StatisticsImpl object.

To see the statistics printed in the log, you need to set the following logger:

<logger name="org.hibernate.engine.internal.StatisticalLoggingSessionEventListener" level="info"/>

Afterward, Hibernate is going to print the following log entries:

INFO  [Alice]: o.h.e.i.StatisticalLoggingSessionEventListener - Session Metrics {
    307100 nanoseconds spent acquiring 1 JDBC connections;
    55100 nanoseconds spent releasing 1 JDBC connections;
    13868600 nanoseconds spent preparing 3 JDBC statements;
    3504100 nanoseconds spent executing 3 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    53460600 nanoseconds spent executing 1 flushes (flushing a total of 3 entities and 2 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

Cool, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

The statistics mechanism is a very useful way to get a better insight into Hibernate internal workings. While the Statistics object is used to read the metrics, the StatisticsImplementor is useful when you want to customize the way the metrics are collected.

FREE EBOOK

8 Comments on “A beginner’s guide to Hibernate Statistics

  1. Thanks for this article, and yep, would look forward to follow-up on exposing via jmx. Subscribing. 🙂

  2. Hi Vlad, very good post!

    Do you know if there’s any tool or library that can consumes those statistics and generate graphs?

    • You can export them via JMX. I’ll write an article about that.

      • I am waiting for your article. Thanks.

      • I’m interested to see how you plan to do it. I’ve got a derivate from the old StatisticsServiceMBean but that approach doesn’t support increment time histograms (codahale metrics etc.) out-of-the-box and polling the JMX to generate approximations these doesn’t sound that appealing.

        While I was looking at the codebase, I started to wonder whether it would possible to replace the private LongAdder and AtomicLongs from the abstract classes of stats with protected fields implementing LongConsumer and LongSupplier (or something similar) as well as having possibility to replace the direct references to implementations with abstract classes.

        Having both field and class initializations behind factory methods would lessen the amount of “ceremonial code” required to create push for histograms by being able to utilize the already existing abstract classes without having to craft such from the scratch.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.