A beginner’s guide to JPA and Hibernate query hints
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
In this article, we are going to see what JPA query hints are, and get to know all the query hints supported by Hibernate.
Understanding the Hibernate query hints is very important when developing a non-trivial enterprise application since they allow you to customize the way queries are executed by Hibernate.
A beginner’s guide to JPA and Hibernate query hints @vlad_mihalceahttps://t.co/9V7UcuBQ7C pic.twitter.com/LugFjLoWxt
— Java (@java) June 27, 2019
JPA query hints
The Java Persistence API defines the notion of query hint, which unlike what its name might suggest, it has nothing to do with database query hints. The JPA query hint is a Java Persistence provider customization option.
To pass a query hint, the JPA specification defines the setHint
method of the javax.persistence.Query
interface.
javax.persistence.query.timeout
The very first query hint supported by the JPA standard was the javax.persistence.query.timeout
one which defined the number of milliseconds a given JPA query is allowed to run for. Behind the scenes, this hint will instruct Hibernate to call the PreparedStatement.setQueryTimeout
method for the associated SQL query that gets executed.
The following example shows you how to set up the javax.persistence.query.timeout
JPA query hint.
List<Post> posts = entityManager .createQuery( "select p " + "from Post p " + "where lower(p.title) like lower(:titlePattern)", Post.class) .setParameter("titlePattern", "%Hibernate%") .setHint("javax.persistence.query.timeout", 50) .getResultList();
javax.persistence.fetchgraph
JPA 2.1 introduced the javax.persistence.fetchgraph
query hint as well to provide a query-specific fetch graph that overrides the default fetch plan defined by the entity mapping.
According to the JPA specification, the javax.persistence.fetchgraph
query hint should fetch eagerly only the associations that are explicitly specified by the currently provided fetch graph while the remaining association should be fetched lazily.
However, because lazy fetching is a non-mandatory requirement, the Hibernate behavior for the javax.persistence.fetchgraph
query hint is different as the associations not specified by the provided fetch graph are fetched according to their entity mapping fetching strategy.
The javax.persistence.fetchgraph
query hint can be specified as follows:
PostComment comment = entityManager .find( PostComment.class, 1L, Collections.singletonMap( "javax.persistence.fetchgraph", entityManager.getEntityGraph("PostComment.post") ) );
javax.persistence.loadgraph
JPA 2.1 introduced the javax.persistence.loadgraph
query hint as well to provide a query-specific fetch graph that overrides the default fetch plan defined by the entity mapping.
According to the JPA specification, the javax.persistence.loadgraph
query hint should fetch eagerly only the associations that are explicitly specified by the currently provided fetch graph while the remaining association should be fetched according to their mapping fetching strategy.
The javax.persistence.loadgraph
query hint can be specified as follows:
PostComment comment = entityManager .find( PostComment.class, 1L, Collections.singletonMap( "javax.persistence.loadgraph", entityManager.getEntityGraph("PostComment.post") ) );
For more details about JPA Entity Graphs, check out this article.
Hibernate query hints
Unlike JPA, Hibernate offers multiple query hints you can use to customize the execution of a given query. To simplify the way you have to reference a given query hint, Hibernate offers the QueryHints
class that looks as follows:
The Hibernate query hints defined by the QueryHints
class can be summarized as follows:
Query hint name | QueryHints constant | Description |
---|---|---|
org.hibernate.cacheMode | CACHE_MODE | Equivalent to setCacheMode method of org.hibernate.query.Query |
org.hibernate.cacheRegion | CACHE_REGION | Equivalent to setCacheRegion method of org.hibernate.query.Query |
org.hibernate.cacheable | CACHEABLE | Equivalent to setCacheable method of org.hibernate.query.Query |
org.hibernate.callable | CALLABLE | Useful for named queries that need to be executed using a JDBC CallableStatement |
org.hibernate.comment | COMMENT | Equivalent to setComment method of org.hibernate.query.Query |
org.hibernate.fetchSize | FETCH_SIZE | Equivalent to setFetchSize method of org.hibernate.query.Query |
org.hibernate.flushMode | FLUSH_MODE | Equivalent to setFlushMode method of org.hibernate.query.Query |
hibernate.query.followOnLocking | FOLLOW_ON_LOCKING | Override the useFollowOnLocking method of org.hibernate.dialect.Dialect |
org.hibernate.lockMode | NATIVE_LOCKMODE | Specify a custom javax.persistence.LockModeType or org.hibernate.LockMode for the current query |
hibernate.query.passDistinctThrough | PASS_DISTINCT_THROUGH | Prevent the JPQL or Criteria API DISTINCT keyword from being passed to the SQL query |
org.hibernate.readOnly | READ_ONLY | Equivalent to setReadOnly of org.hibernate.query.Query |
org.hibernate.timeout | TIMEOUT_HIBERNATE | Equivalent to setTimeout of org.hibernate.query.Query . The timout value is specified in seconds. |
javax.persistence.query.timeout | TIMEOUT_JPA | Equivalent to setTimeout of org.hibernate.query.Query . The timout value is specified in milliseconds. |
Although you can customize the query execution using the Hibernate-specific query interfaces (e.g. org.hibernate.query.Query
), if you’re using the JPA java.persistence.Query
interface, you don’t have to unwrap it to get access to the aforementioned query customization options since you can just use the equivalent Hibernate query hint.
Therefore, to fetch the Post
entities in read-only mode, you just have to use the org.hibernate.readOnly
query hint, as in the following example:
List<Post> posts = entityManager .createQuery( "select p " + "from Post p", Post.class) .setHint(QueryHints.READ_ONLY, true) .getResultList();
The hibernate.query.passDistinctThrough
query hint is the only way you can prevent the JPQL DISTINCT keyword from being pushed to the associated SQL query.
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 JPA query hints mechanism allows you to customize the way a given query is executed by Hibernate. For instance, you can specify a timeout threshold or specify that the returned entities should be fetched in read-only mode.
