Advanced Java Connection and Transaction Demarcation and Propagation

Jakob Jenkov
Last update: 2014-05-21

Connection and transaction demarcation and propagation isn't as easy as it may seem. You may have already read the text Dao Design Problems which talks about the basic design problems in DAO design: Connections have a tendency to escape the DAO layer, so the life span of the connection / transaction can be demarcated. If you have not read this text, it may be a good idea to do so, before reading on here.

Scoping = Demarcation

In this text I will sometimes use the terms "scoping" and "demarcation" interchangeably. By "scope" I mean the scope of a connections life span, or the scope of a transaction. A scope is demarcated using a start scope and end scope demarcation.

Typical Program Flow: Event - Control - Dao - Connection

The typical flow of control in an application that uses a database is a bit like this:


Event Control DAO Connection

First an event occurs, for instance a user clicks a button in a desktop application, or an HTTP request arrives at your web application. Second a control processes the event. This control typically determines if it is necessary to access the database. If it is, the control accesses the DAO layer. The DAO layer accesses a standard JDBC database connection to carry out its work.

Control Method Scope

A control may either call one or more methods on the same DAO instance, or call methods on multiple DAO instances. This means that the scope of the connection is typically the same as the scope of the control method reacting to the event.

Imagine this call stack:

Event
  --> Control.execute()
      --> PersonDao.readPerson()
          --> Connection //read person via SQL.

      --> ProjectDao.readProject()
          --> Connection // read project via SQL.

      --> ProjectDao.assignPersonToProject()
          --> Connection // update project assignments via SQL.

Notice how two different DAO's were called from inside the control's execute() method. In total the database connection was accessed three times. You don't want every DAO method to open and close its own database connection, let alone run inside its own transaction. You want each DAO method to share the same connection and perhaps even the same transaction.

To make this happen you need to obtain a database connection (or equivalent object) inside the Control.execute() method, and pass this connection (or other object) to each DAO. For instance it could look like this sketchy pseudo code:

Control.execute() {
  Connection connection = null;

  try{
    getConnection();
    PersonDao  personDao  = new PersonDao(connection);
    ProjectDao projectDao = new ProjectDao(connection);

    Person  person  = personDao.readPerson(...);
    Project project = projectDao.readProject(...);

    projectDao.assignPersonToProject(person, project);
  } finally {
    if(connection != null) connection.close();
  }
}

Now, why is this annoying to have to do?

Well, because now the DAO layer does not hide the Connection anymore. In addition, you cannot easily inject the DAO's into the Control instance. Well, you could if you call a setConnection() method on them afterwards. But it's preferable to have fully configured DAO instances injected.

The DaoManager Solution

In the text Dao Manager I described a solution to the problem mentioned in the previous section, that the database connection leaks out of the DAO's.

The DaoManager is a class that you put in between the Control and the DAO's. The DaoManager is born with a single database connection (or the ability to obtain one lazily). From the DaoManager the Control can then access all the DAO's. Each DAO is created lazily and cached, and have the connection from the DaoManager injected into its constructor.

Here's a rough sketch of how it looks:


Control DaoManager Dao A Dao B Dao C

This way the DaoManager can be injected into the Control completely configured. If the DaoManager has a DataSource injected (or a PersistenceManager in Butterfly Persistence), the DaoManager can obtain a connection lazily, in case a connection is needed by any of the DAO's.

The DaoManager also has a method that can execute some code and close the connection afterwards, or commit the transaction. Here is how a call to that method looks from inside the Control:


public class Control {

  protected DaoManager daoManager = null;

  public Control(DaoManager daoManager){
    this.daoManager = daoManager;
  }

  public void execute(){

    this.daoManager.executeAndClose(new DaoCommand(){
        public Object execute(DaoManager manager){
          daoManager.getDaoA();
          daoManager.getDaoB();
          daoManager.getDaoC();
       }
    });
  }

}

Once the DaoManager.executeAndClose() method finishes the database connection inside the DaoManager is closed.

For more information on how the DaoManager works, see the text Dao Manager.

Even though the DaoManager solution seems to work fine in most cases, there are cases where it is not enough. I'll look at these cases in the next section.

DaoManager Limitations

As you can see from the code example in the previous section, the scope of the connection when managed by the DaoManager.executeAndClose() is the boundaries of the executeAndClose() method.

But, what if you have more than one Control that will respond to a given event, and you want to share the database connection between controls?

For instance, what if your controls were organized into a tree, like they can be if you are using Butterfly Web UI or Wicket? Like this:


Event Control DAO Connection Control DAO Connection Control DAO Connection

Or maybe even harder, into a list of independent controls listening for the same event. This could be the case in a desktop application if each control is registered independently as listeners on e.g. a button. Here is how that would look:


Event Control DAO Connection Control DAO Connection Control DAO Connection

In these cases it is hard (if not impossible) to use the DaoManager solution.

Think about it for a second or two. Imagine if there was a DaoManager between the Control and DAO's in the two diagrams above.

It is the DaoManager's executeAndClose() method that demarcates the life span of the underlying connection. If you call this method from each control's execute() method (or whatever the central execution method in your controls is called), each control will open and close a connection separately. That is exactly what we are trying to avoid. Or, at least be able to avoid whenever desired. There could of course be situations where you actually want the controls to use independent connections.

The ScopingDataSource Solution

The ScopingDataSource is a different solution to the problem of demarcating connection and transaction life spans (scopes). It is a solution I implemented for my persistence API, Mr Persister, which is now continued in the Butterfly Persistence API. The ScopingDataSource will be moved to Butterfly Persistence from around version 5.2.0 or 5.4.0 which will be released in 2009.

The ScopingDataSource is an implementation of the standard Java interface javax.sql.DataSource. It is a wrapper around a standard DataSource implementation. This means, that instead of calling directly to your DataSource instance to obtain a connection, you call the ScopingDataSource instead.

The ScopingDataSource has the following scope demarcation methods:

    beginConnectionScope();
    endConnectionScope();

    beginTransactionScope();
    endTransactionScope();
    abortTransactionScope();

These methods demarcate the beginning and end of connection and transaction life spans. The methods are explained in a little more detail in the following sections.

Connection Scoping

Your control start out by calling the ScopingDataSource.beginConnectionScope(). Once this method is called, whenever the tread that called this method calls the ScopingDataSource.getConnection() method, the same connection instance is returned.

The Connection that is returned by the ScopingDataSource is also a wrapper around the real Connection instance. This ScopingConnection ignores all calls to the close() method, so the underlying connection can be reused.

When you are ready to close the connection your control calls the ScopingDataSource.endConnectionScope(), and the currently open connection (if any) is closed. From here on the ScopingDataSource behaves just like a regular DataSource, returning a new Connection for every call to getConnection().

The principle is illustrated here:


Control ScopingDataSource beginConnectionScope() Dao A Dao B endConnectionScope() getConnection() getConnection()

The calls to beginConnectionScope() and endConnectionScope() do not have to be located within the same method, nor within the same class. They just have to be located within the same thread of execution.

The advantage of the ScopingDataSource solution is that your DAO's don't need to know anything about it. Your DAO's can take DataSource in their constructor, and obtain a connection from that. It doesn't matter whether your DAO's obtain a single connection internally in the DAO and share it between methods, or obtain a new connection and close it again inside each method. Your DAO's can also take a Connection in their contructor, and still not have to know anything about how the scoping works. In short, you get a lot of freedom in your DAO design.

The only time the wrapped connection can cause problems is if you are using an API that requires the original connection. Oracle's Advanced Queue (AQ) API has this problem (or at least had it in 2005). The API did not work with connection wrappers. Only with Oracle's own Connection implementation. Dead annoying!

Here is a code sketch showing how the ScopingDataSource is used:


public class DBControlBase {

 protected ScopingDataSource scopingDataSource = null;

 public DBControlBase(ScopingDataSource dataSource){
   this.scopingDataSource = dataSource;
 }

 public void execute(){
   Exception error = null;
   try{
     this.scopingDataSource.beginConnectionScope();
     doExecute();

   } catch(Exception e){
      error = e;
   } finally {
      this.scopingDataSource.endConnectionScope(e);
   }
 }

 public void doExecute() {
   PersonDao  personDao  = new PersonDao (this.scopingDataSource);
   ProjectDao projectDao = new ProjectDao(this.scopingDataSource);

   //do DAO work here...
 }

}

You can just extend the DBControlBase and override the doExecute() method, then all connection scoping is done for you.

But wait, isn't the effect pretty much the same as what you got with the DaoManager? Yes, in the code sketch above it is. But you have more options now. The connection scope demarcation method calls do not have to be embedded inside your control. They can be called outside the Control.execute() method too, or inside a parent control. Here is how that could look:


Control ScopingDataSource beginConnectionScope() endConnectionScope() Control Dao A Dao B getConnection() getConnection()

Once you start playing around with this idea, there are lots of possibilities.

Another option is to use method interception, like Spring does. If your Control class implements an interface, you can implement a Dynamic Proxy which implements the same interface. Your control is then wrapped in this dynamic proxy. When the execute() method is called on the control interface, this dynamic proxy will call the beginConnectionScope(), then call your controls execute() method, and finally the endConnectionScope(). Here is a code sketch of a dynamic proxy:

public class ConnectionScopeProxy implements InvocationHandler{

  protected ScopingDataSource scopingDataSource = null;
  protected Object            wrappedTarget     = null;

  public ConnectionScopeProxy(
        ScopingDataSource dataSource,
        Object            target) {

    this.scopingDataSource = dataSource;
    this.wrappedTarget     = target;
  }


  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {

    Exception error = null;

    try{
      this.scopingDataSource.beginConnectionScope();
      method.invoke(this.wrappedTarget, args);
    } catch(Exception e){
      error = e;
    } finally {
      this.scopingDataSource.endConnectionScope(error);
    }

  }
}

Transaction Scoping

Transaction scoping is done in pretty much the same way as connection scoping. The only difference is that you call beginTransactionScope() and endTransactionScope() instead.

When a connection is obtained from the ScopingDataSource while inside a transaction scope, connection.setAutoCommit(false) is called. That puts the connection into transactional state.

When a transaction scope ends the transaction is committed and the connection closed. If an error occurs during the commit, the transaction is automatically aborted. If an exception is thrown before the endTransactionScope() method is called, you should catch that exception and call abortTransactionScope(Exception) with that exception.

Here is a code sketch:


Exception error = null;
try{
  scopingDataSource.beginTransactionScope();

  //do DAO work inside transaction
} catch(Exception e){
  error = e;
} finally{
  if(error == null){
    scopingDataSource.endTransactionScope();
  } else {
    scopingDataSource.abortTransactionScope(e);
  }
}

Transaction scopes can be nested inside connection scopes. If a transaction scope is nested inside a connection scope, ending the transaction scope will not close the connection. That way you can nest multiple transaction scopes within a single connection scope, resulting in multiple transactions being committed on the same connection.

Closing this text I will show a transaction scope dynamic proxy code sketch:

public class TransactionScopeProxy implements InvocationHandler{

  protected ScopingDataSource scopingDataSource = null;
  protected Object            wrappedTarget     = null;

  public TransactionScopeProxy(
        ScopingDataSource dataSource,
        Object            target) {

    this.scopingDataSource = dataSource;
    this.wrappedTarget     = target;
  }


  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {

    Exception error = null;

    try{
      this.scopingDataSource.beginTransactionScope();
      method.invoke(this.wrappedTarget, args);
    } catch(Exception e){
      error = e;
    } finally {
      if(error == null){
        this.scopingDataSource.endTransactionScope();
      } else {
        this.scopingDataSource.abortTransactionScope(error);
      }
    }
  }
}

Jakob Jenkov

Featured Videos

Java Generics

Java ForkJoinPool

P2P Networks Introduction



















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next