Hypersistence Optimizer Runtime Scanner
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
Introduction
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 User Guide provides you with step-by-step instructions and references to existing demo applications on GitHub that will help you set up the new runtime scanner is no time.
Implementing the runtime scanner required a lot of work, but I’m glad I managed to bring you this awesome functionality.
The amount of work needed to add support for runtime scanning in @Hypersistence Optimizer.
— Vlad Mihalcea (@vlad_mihalcea) February 6, 2020
Looking forward to finishing the remaining items scheduled for the 2.0 release.
Stay tuned!https://t.co/q9TnXs1H1p pic.twitter.com/pryZ71hZpx
Intercepting queries
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 .createQuery( "select p " + "from Post p ", Post.class) .setMaxResults(5) .getResultList();
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.
Or, when running the following Criteria API:
CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<Post> criteria = builder.createQuery(Post.class); Root<Post> post = criteria.from(Post.class); post.fetch("comments"); criteria.select(post).distinct(true); List<Post> posts = entityManager .createQuery(criteria) .getResultList();
Hypersistence Optimizer will spot that you forgot to provide the PASS_DISTINCT_THROUGH
query hint:
ERROR [main]: Hypersistence Optimizer - CRITICAL - PassDistinctThroughEvent - The [ 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"); entityManager.persist(post); int postCount = ( (Number) session .createSQLQuery( "SELECT count(*) " + "FROM post ") .uniqueResult() ) .intValue(); assertEquals(0, postCount);
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].
Cool, right?
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:
List<Event> events = hypersistenceOptimizer.getEvents();
Enabling and disabling scanners
You can now enable or disable the mapping, configuration, and runtime scanner from the JpaConfig
and HibernateConfig
instances that you are passing to the HypersistenceOptimizer
constructor.
This mechanism will give you better control over what needs to be scanned when you cannot use a singleton HypersistenceOptimizer
instance for all integration tests.
HTML and PDF documentation
The assembly pack you are downloading from the product pack contains the Installation and User Guides in both HTML and PDF formats.
This will allow you to read the documentation when you are offline or print the supported event list, which is a very valuable resource in itself.
I'm running an online workshop on the 11th of October about High-Performance SQL.If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
The 2.0 version is only the beginning. I’m looking forward to adding support for detecting slow queries, queries that fetch way too much data, and 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!
