Imagine having a tool that can automatically detect JPA and Hibernate performance issues.
Hypersistence Optimizer is that tool!
The 2.0 version of the Hypersistence Optimizer has just arrived, and it comes with a runtime scanner that is capable of analyzing your JPA and Hibernate data access operations and queries and give you tips about how you can speed up your application.
Along with the JPA and Hibernate mapping and configuration scanners, the runtime scanner makes Hypersistence Optimizer an indispensable tool for building High-Performance Java Persistence applications.
Decorating the Persistence Unit
To be able to intercept JPA EntityManager and Hibernate Session data access operations, the Persistence Unit (e.g., EntityManagerFactory or SessionFactory) needs to be decorated with runtime scanning capabilities.
The runtime scanner is able to intercept any type of query you are executing, from JPQL to Criteria API, legacy Criteria, and native SQL queries.
For instance, when executing the following JPQL query:
List<Post> posts = entityManager
"select p " +
"from Post p ", Post.class)
Hypersistence Optimizer will generate the following event:
ERROR [main]: Hypersistence Optimizer - CRITICAL - PaginationWithoutOrderByEvent -
The [select p from Post p ] query uses pagination without an ORDER BY clause.
Therefore, the result is not deterministic since SQL does not guarantee
any particular ordering unless an ORDER BY clause is being used.
We forgot to add an ORDER BY clause, and, for this reason, the offset pagination result set would not be deterministic.
ERROR [main]: Hypersistence Optimizer - CRITICAL - PassDistinctThroughEvent -
select distinct p
from Post as p
inner join fetch p.comments as pc
query uses DISTINCT to deduplicate the returned entities.
However, without setting the [hibernate.query.passDistinctThrough]
JPA query hint to [false], the underlying SQL statement
will also contain DISTINCT, which will incur extra sorting and
duplication removal execution stages.
Analyzing the Persistence Context
As I explained in this article, when bootstrapping Hibernate natively like it’s the case when using the Spring SessionFactoryBean, the Hibernate Session is not flushed automatically prior to executing a native SQL query.
So, when counting the post table records with the following SQL query, we can see that the postCount value is going to be 0.
Post post = new Post();
post.setTitle("High-Performance Java Persistence");
int postCount = (
"SELECT count(*) " +
"FROM post ")
Hypersistence Optimizer will trigger the following event when executing the above query:
ERROR [main]: Hypersistence Optimizer - CRITICAL - FlushModeAutoEvent -
The Hibernate Session uses the legacy [FlushMode.AUTO] strategy,
that does not guarantee read-your-writes consistency for native SQL queries.
When using the default [FlushMode.AUTO], Hibernate does not flush
the Persistence Context prior to executing an SQL query,
so the pending entity changes will not be visible to the query execution.
Consider setting the current Session or Query [flushMode] property
to the value of [ALWAYS], or add the [org.hibernate.flushMode]
Hibernate configuration setting with the value of [always].
Simplifying the event retrieval
Previously, to get the list of Event objects that were triggered by Hypersistence Optimizer, you had to provide a ListEventHandler. Since version 2.0, you can fetch all events like this:
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
The 2.0 version is only a beginning. I’m looking forward to adding support for detecting slow queries, queries that fetch way too much data, Persistence Context instance fetching entities in the read-write mode without applying any modification.
Enjoy running your Java database application at high speed and stay tuned for more!