1
votes

Currently in my JavaEE application-server with local and remote EJB's, MDB's (Singleton and Stateless) I am using JDBC-Transactions for Hibernate Core.

Managing myself all my opening and closing, committing of hibernate sessions and transactions can lead to connection-leaks and uncomitted transactions.
Especially in the case of programming errors resulting in custom or unchecked exceptions that are not catched and thrown to the remote client.

What is the easiest or best way to make sure my hibernate sessions are closed and transactions rolled back in case an error is thrown?

Use container-managed transactions (CMT) or is it possible to close sessions within an interceptor that is called on return of any EJB methods?

One easy way would be to wrap the session-scoped usage in a try-catch block and catching any type of Exception, but a general approach with less code would be favored.

Edit: Remote EJB Example

  • My low-level Hibernate DAO does closes connection and rolls back transaction on thrown exceptions. Problematic is business-logic between DAO accesses in case connection is still open.*

    public void doSomething(Foo foo) throws Exception
    {
        // open session and transaction
        Session session = DAO.openSession();
    
        // retrieve data
        Bar bar = DAO.get(session, ...) 
    
        // call other methods which throws an exception resulting in open connection
        doOtherStuff(foo, bar)
    
        DAO.save(session, foo);
    
        // commit transaction
        DAO.closeAndCommitSession(session);
    }
    

Right now i am using a big try-catch-finally:

    public void doSomething(Foo foo) throws Exception
    {
        // open session and transaction
        Session session = DAO.openSession();
        try
        {
            // retrieve data
            Bar bar = DAO.get(session, ...) 

            // call other methods which throws an exception resulting in open connection
            doOtherStuff(foo, bar)

            DAO.save(session, foo);
        }
        catch (final Exception e)
        {
            DAO.rollBackTransaction(session);
            throw e;
        }
        finally
        {
            DAO.closeAndCommitSession(session);
        }
    }
1

1 Answers

2
votes

Generally speaking this question is about doing resource management correctly and in a simple way. This always requires two ingredients: A simple API and discipline to use this API everywhere.

Probably not a concrete solution for your use case using Hibernate, but just to show the general idea: Use the loan pattern:

public interface ConnectionRunnable {
    public void run(Connection conn) throws SQLException;
}

This code would be far more elegant, if you used Groovy or Scala (with closures). In that case this interface is not needed.

Anyway, everywhere you need a connection, use something like this:

    public static void withConnection(final String dataSourceName,
            ConnectionRunnable runnable) throws SQLException, NamingException {
        final InitialContext ic = new InitialContext();
        final DataSource ds = (DataSource) ic.lookup(dataSourceName);

        final Connection conn = ds.getConnection();
        try {
            runnable.run(conn);
        } finally {
            conn.close();
        }
    }

to be used like this in your EJBs:

ConnectionUtils.withConnection("java:/jdbc/sandbox", new ConnectionRunnable() {
    public void run(Connection conn) throws SQLException {
        // Do something with the connection
    }
});
  • Using the loan pattern, you simply can't forget to close a connection
  • In case of a SQLException or NamingException you can centrally decide how to handle it correctly: commit or rollback manually using BMT. Use setRollbackOnly or throw a SystemException to trigger the container (using CMT) etc. If there is no special reason, use CMT.
  • Even if you change your error handling later, it's only one code fragment.

Update: Overhead of using the ConnectionRunnable interface.

The ConnectionRunnable is a "typing-overhead" only: Groovy and Scala implement closures the same way, and the run-time overhead is negligible. Java 8 will implement closures probably the same way.

In this scenario the costs of committing or rolling back on a database transaction will be much higher anyway.

It's just looking odd from the Java/procedural/usual perspective, and the idea of code reuse with respect to exception handling may look odd as well.

Using Scala it looks like this at the usage site (translated literally):

ConnectionUtils.withConnection("java:/jdbc/sandbox") {
  conn =>
    // Do something with the connection
}

but under the hood it uses something like an anonymous class on the JVM as well .