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.

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 QueryHints class

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 setFetchSizemethod 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 20-21 and 23-24 of November about High-Performance Java Persistence.

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.

Transactions and Concurrency Control eBook

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.