Question

I have developed a JDBC connection pool using synchronized methods like getConnection and returnConnection. This works well and it is fast enough for my purposes. Now the problem happens when this connection pool has to be shared in other packages of our application and so other developers will make use it as well. I feel it is a bit confusing as they always need to perform a returnConnection and I am afraid they may forget to do so.

Thinking about it I came up with the idea to expose only only method in my connection pool and force the other developers to encapsulate their code and so I handle the getConnection / returnConnection inside the connection pool.

It would be something like this:

public MyConnectionPool {

private Connection getConnection() {
    //return connection
}

private void returnConnection(Connection connection) {
     //add connection to list
}

public void executeDBTask(DBTaskIF task) {
    Connection connection = getConnection();
    task.execute(connection);
    returnConnection(connection);
}

}

where:

public interface DBTaskIF {
  public execute(Connection connection);
}

with an example of this DBTaskIF:

connectionPool.executeDBTask( new DBTaskIF() {
public void execute(Connection connection) {

PreparedStatement preStmt = null;
try {
    preStmt = connection.prepareStatement(Queries.A_QUERY);
    preStmt.setString(1, baseName);
    preStmt.executeUpdate();
} finally {
    if(preStmt!=null) {
    try {
        preStmt.close();
    } catch (SQLException e) {
        log.error(e.getStackTrace());
    }
}
}}});

I hope you can get the idea. What I want to know is your opinion about this approach. I want to propose this to the development team and I worry some one comes up saying that this is not standard or OOP or something else... Any comments are much appreciated.

Was it helpful?

Solution

I feel it is a bit confusing as they always need to perform a returnConnection and I am afraid they may forget to do so.

Thinking about it I came up with the idea to expose only only method in my connection pool and force the other developers to encapsulate their code and so I handle the getConnection returnConnection inside the connection pool.

I'm concerned with this statement. APIs should not (never?) assume that whoever uses them will do so in some way that is not enforced contractually by whichever method it exposes.

And java.sql.Connection is a widely used interface so you'll be making enemies by telling people how to use it with your pool.

Instead, you should assume that your Connection instances will be used correctly, i.e., that they will be closed (connection.close() in a finally block) once their use is over (see, for instance, Connecting with DataSource Objects):

   Connection con;
   PreparedStatement stmt;
   try {
        con = pool.getConnection();
        con.setAutoCommit(false);
        stmt = con.prepareStatement(...);
        stmt.setFloat(1, ...);
        stmt.setString(2, ...);
        stmt.executeUpdate();

        con.commit();
        stmt.close();
    } catch (SQLException e) {
        con.rollback();
    } finally {
        try {
           if(con!=null) 
               con.close();
           if(stmt!=null) {
               stmt.close();
        } catch (SQLException e) {
             ...
        } finally {

        }
    }

And the Connection implementation of your pool should be recycled when closed.

I second @lreeder's comment that you're really reinventing the wheel here and that most connection pools already available are definitely fast enough for most purposes, and underwent many fine tweakings over time. This also applies to embedded databases.

OTHER TIPS

Disclaimer; this is just my opinion, but I have written custom connection pools before. I find Java code where you have to create inner class impls a little clunky. However in Java8 lambda or Scala anonymous functions this would be a clean design. I probably would just expose returnConnection() as a public method and allow callers to use it directly.

Third option: use a utility class that takes care of most of the administration.
Not only forgetting to close a Connection can cause trouble, but also forgetting to close a Statement or a Resultset can cause trouble. This is similar to using various IO streams in a method: at some point you make an extra utility class in which you register all opened IO streams so that if an error occurs, you can call close in the utility class and be sure that all opened IO streams are closed.
Such a utility class will not cover all use cases but there is always the option to write another one for other (more complex) use cases. As long as they keep the same kind of contract, using them should just make things easier (and will not feel forced).

Wrapping or proxying a Connection to change the behavior of close to return the Connection to the pool is in general how connection pools prevent connections from actually being closed. But if a connection pool is not used, the application is usually written in a different manner: a connection (or two) is created (at startup) and used wherever a query is executed and the connection is only closed when it is known that a connection is not needed for a while (at shutdown). In contrast, when a pool is used, the connection is "closed" as soon as possible so that other processes can re-use the connection. This together with the option to use a utility class, made me decide to NOT wrap or proxy a connection, but instead let the utility class actually return the connection to the pool if a pool was used (i.e. not call connection.close() but call pool.release(connection)). Usage example of such a utility class is here, the utlity class itself is here.

Proxying causes small delays which is why for example BoneCP decided to wrap Connection and Datasource (wrapping causes very little overhead). The Datasource interface changes with each Java version (at least from 1.6 to 1.7) which means the code will not compile with older/newer versions of Java. This made me decide to proxy the Datasource because it is easier to maintain, but it is not easy to setup (see the various proxy helper classes here). Proxying also has the drawback of making stack-traces harder to read (which makes debugging harder) and sometimes makes exceptions disappear (I have seen this happen in JBoss where the underlying object threw a runtime exception from the constructor).

tl;dr If you make your own specialized pool, also deliver a utility class which makes it easy to use the pool and takes care of most of the administration that is required (like closing used resources) so that it is unlikely to be forgotten. If a utility class is not an option, wrapping or proxying is the standard way to go.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top