Hibernate Session doWork and doReturningWork methods
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
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
);
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.






