Hibernate Session doWork and doReturningWork methods

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, I’m going to explain how the Hibernate Session doWork and doReturningWork methods work, and when you should use them.

Hibernate Session doWork

The Hibernate Session provides a doWork method that has the following signature:

void doWork(Work work) throws HibernateException;

And the Work interface is defined as follows:

public interface Work {
    void execute(Connection connection) throws SQLException;
}

So, the doWork method allows us to get access to the underlying JDBC Connection that’s enlisted in the current local (JDBC) or global (JTA) transaction.

Normally, you don’t need to use the JDBC Connection object to execute SELECT, INSERT, UPDATE, or DELETE statements with JPA and Hibernate, since you can use the JPA Query API for that.

However, there are several JDBC features that can only be enabled via the Connection object, like setting a global statement timeout threshold via the setNetworkTimeout method.

Assuming that we created the following Java Executor:

private Executor executor = Executors.newFixedThreadPool(connectionPoolSize);

We can now set the global statement timeout to the value of 1000 milliseconds, like this:

Session session = entityManager.unwrap(Session.class);

session.doWork(connection -> {
    connection.setNetworkTimeout(
        executor,
        (int) TimeUnit.SECONDS.toMillis(1)
    );
});

Notice that since the Work interface is a @FunctionalInterface, we can pass a Java lambda to the doWork method call.

So, now, if an SQL statement takes more than 1 second, it will fail with a SocketTimeoutException:

try {
    entityManager.createNativeQuery(
        "select pg_sleep(2)"
    )
    .getResultList();
} catch (Exception e) {
    assertTrue(
        SocketTimeoutException.class.isInstance(
            ExceptionUtil.rootCause(e)
        )
    );
}

Hibernate Session doReturningWork

The Hibernate Session also provides a doReturningWork method that has the following signature:

<T> T doReturningWork(ReturningWork<T> work) throws HibernateException;

And the ReturningWork interface is defined as follows:

public interface ReturningWork<T> {
    public T execute(Connection connection) throws SQLException;
}

So, unlike the doWork method, the doReturningWork allows us to return an object to the method caller.

For example, we can use the doReturningWork method to get the current transaction isolation level:

Session session = entityManager.unwrap(Session.class);

int isolationLevel = session.doReturningWork(
    connection -> connection.getTransactionIsolation()
);

assertEquals(
    Connection.TRANSACTION_READ_COMMITTED, 
    isolationLevel
);

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

While most of the time, you can use the JPA or Hibernate-specific API to execute SQL statements or call database procedures or functions, the doWork and doReturningWork Hibernate Session methods give access to the underlying JDBC Connection, therefore, allowing us to execute any operation that is possible using the JDBC API.

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.