Jakarta Commons DBCP Tutorial

A quick code preview showing working example of using one of the best-performing JDBC connection pools in Java.


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;

-- snippet --


        Connection conn = null; PreparedStatement p_stmt = null;

        try {
            conn = ConnectionManager.ds.getConnection();

            p_stmt =  conn.prepareStatement(
            	"INSERT INTO articles (text,url,content) VALUES (?,?,?)");
            p_stmt.setBytes(1, (new String("jsdhgkdjshgkjdshkg")).
            	getBytes("UTF8") );
            p_stmt.setString(2, "http://dsfsfsfdsfdsfda");
            p_stmt.setBytes(3, (new String("aadada")).getBytes("UTF8") );

            p_stmt.executeUpdate();
        }  catch(Exception e) {
                LOG.debug("Failed to execute a JDBC task: ", e );
            } finally {
                try {
                    p_stmt.close();
                    conn.close();
                }  catch ( Exception ex) {
                    LOG.error ( "Failed to finalize JDBC task: ", ex );
                }
            }

-- snippet --

config.xml:

<config>
    <dbDriverName>com.mysql.jdbc.Driver</dbDriverName>
    <dbUser>root</dbUser>
    <dbPassword>whatever</dbPassword>
 <dbURI><![CDATA[jdbc:mysql://localhost/testdb?
 useUnicode=true&characterEncoding=UTF-8&autoReconnect=true]]></dbURI>
    <dbPoolMinSize>30</dbPoolMinSize>
    <dbPoolMaxSize>70</dbPoolMaxSize>
</config>

ConnectionManager.java

package ge.ika.tutorial.dbcp;

import ge.ika.tutorial.dbcp.util.Configuration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.dbcp.*;

import javax.sql.DataSource;
import java.util.Date;
import java.sql.*;

/**
 * @ inadareishvili
 */
public class ConnectionManager {

    private static final Log LOG = LogFactory
         .getLog(ConnectionManager.class);

    public static DataSource ds = null;
    private static GenericObjectPool _pool = null;

    /**
    *  @param config configuration from an XML file.
    */
    public ConnectionManager(Configuration config)
    {
        try
        {
            connectToDB( config );
        }
        catch(Exception e)
        {
            LOG.error( "Failed to construct 
            ConnectionManager", e );
        }
    }

    /**
    *  destructor
    */
    protected void finalize()
    {
        LOG.debug("Finalizing ConnectionManager");
        try
        {
            super.finalize();
        }
        catch(Throwable ex)
        {
            LOG.error( "ConnectionManager finalize failed 
            to disconnect from mysql: ", ex );
        }
    }


    /**
    *  connectToDB - Connect to the MySql DB!
    */
    private void connectToDB( Configuration config ) {

        try
        {
            java.lang.Class.forName( config.getDbDriverName() ).newInstance();
        }
        catch(Exception e)
        {
            LOG.error("Error when attempting to obtain DB Driver: "
                    + config.getDbDriverName() + " on "
                    + new Date().toString(), e);
        }

        LOG.debug("Trying to connect to database...");
        try
        {
            ConnectionManager.ds = setupDataSource(
                    config.getDbURI(),
                    config.getDbUser(),
                    config.getDbPassword(),
                    config.getDbPoolMinSize(),
                    config.getDbPoolMaxSize() );

            LOG.debug("Connection attempt to database succeeded.");
        }
        catch(Exception e)
        {
            LOG.error("Error when attempting to connect to DB ", e);
        }
    }

    /**
     *
     * @param connectURI - JDBC Connection URI
     * @param username - JDBC Connection username
     * @param password - JDBC Connection password
     * @param minIdle - Minimum number of idel connection in the connection pool
     * @param maxActive - Connection Pool Maximum Capacity (Size)
     * @throws Exception
     */
    public static DataSource setupDataSource(String connectURI, 
	String username, 
	String password,
	int minIdle, int maxActive
	) throws Exception {
        //
        // First, we'll need a ObjectPool that serves as the
        // actual pool of connections.
        //
        // We'll use a GenericObjectPool instance, although
        // any ObjectPool implementation will suffice.
        //
        GenericObjectPool connectionPool = new GenericObjectPool(null);

        connectionPool.setMinIdle( minIdle );
        connectionPool.setMaxActive( maxActive );

        ConnectionManager._pool = connectionPool; 
        // we keep it for two reasons
      // #1 We need it for statistics/debugging
      // #2 PoolingDataSource does not have getPool()
      // method, for some obscure, weird reason.

        //
        // Next, we'll create a ConnectionFactory that the
        // pool will use to create Connections.
        // We'll use the DriverManagerConnectionFactory,
        // using the connect string from configuration
        //
        ConnectionFactory connectionFactory = 
        	new DriverManagerConnectionFactory(connectURI,username, password);

        //
        // Now we'll create the PoolableConnectionFactory, which wraps
        // the "real" Connections created by the ConnectionFactory with
        // the classes that implement the pooling functionality.
        //
        PoolableConnectionFactory poolableConnectionFactory =
                new PoolableConnectionFactory(
        	connectionFactory,connectionPool,null,null,false,true);

        PoolingDataSource dataSource = 
        	new PoolingDataSource(connectionPool);

        return dataSource;
    }

    public static void printDriverStats() throws Exception {
        ObjectPool connectionPool = ConnectionManager._pool;
        LOG.info("NumActive: " + connectionPool.getNumActive());
        LOG.info("NumIdle: " + connectionPool.getNumIdle());
    }

    /**
    *  getNumLockedProcesses - gets the 
    *  number of currently locked processes on the MySQL db
    *
    *  @return Number of locked processes
    */
    public int getNumLockedProcesses()
    {
        int num_locked_connections = 0;
        Connection con = null; 
        PreparedStatement p_stmt = null;  ResultSet rs = null;
        try
        {
            con = ConnectionManager.ds.getConnection();
            p_stmt = con.prepareStatement("SHOW PROCESSLIST");
            rs = p_stmt.executeQuery();
            while(rs.next())
            {
                if(rs.getString("State") != 
                		null && rs.getString("State").equals("Locked"))
                {
                    num_locked_connections++;
                }
            }
        }
        catch(Exception e)
        {
            LOG.debug("Failed to get get 
            	Locked Connections - Exception: " + e.toString());
        } finally {
            try {
                rs.close();
                p_stmt.close();
                con.close();
            }  catch ( java.sql.SQLException ex) {
                LOG.error ( ex.toString() );
            }
        }
        return num_locked_connections;
    }

}

Configuration.java

package ge.ika.tutorial.dbcp.util;

import org.jdom.*;
import org.jdom.input.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import java.io.*;

/**
 * @ inadareishvili
 */
public class Configuration {

    private static final String CONFIG_FILENAME = "config.xml";

    private String dbDriverName = null;
    private String dbUser = null;
    private String dbPassword = null;
    private String dbURI = null;

    private int dbPoolMinSize = 0;
    private int dbPoolMaxSize = 0;

    private static final Log LOG = 
    	LogFactory.getLog( Configuration.class );

    public Configuration() {
        SAXBuilder builder = new SAXBuilder();

        try {

            InputStream is =
            this.getClass().getClassLoader().getResourceAsStream( CONFIG_FILENAME );

            Document doc = builder.build ( is );
            Element root = doc.getRootElement();

            dbDriverName = root.getChild("dbDriverName").getTextTrim();
            dbUser = root.getChild("dbUser").getTextTrim();
            dbPassword = root.getChild("dbPassword").getTextTrim();
            dbURI = root.getChild("dbURI").getTextTrim();
            dbPoolMinSize = 
            	Integer.parseInt( root.getChild("dbPoolMinSize").getTextTrim() );
            dbPoolMaxSize = 
            	Integer.parseInt( root.getChild("dbPoolMaxSize").getTextTrim() );

        }   catch ( Exception ex ) {
            LOG.error( "Could not read configuration file: ", ex );
        }

    }


    public String getDbDriverName() {
        return dbDriverName;
    }

    public String getDbUser() {
        return dbUser;
    }

    public String getDbPassword() {
        return dbPassword;
    }

    public String getDbURI() {
        return dbURI;
    }

    public int getDbPoolMinSize() {
        return dbPoolMinSize;
    }

    public int getDbPoolMaxSize() {
        return dbPoolMaxSize;
    }

    public String toString() {
        MultilineToStringStyle style = new MultilineToStringStyle();
        ReflectionToStringBuilder tsb = 
        	new ReflectionToStringBuilder(this, style);
        return tsb.toString();
    }
}

thanks

Great! A wonderfull dbcp example. Thank you.

Greetings from amsterdam

Excellent example

Thanks for posting such a fantastic example.

Thank for this example.

Thank for this example .
This can help me so much.

Best help on dbcp

I guess, the worst side of dbcp is existence of no detailed and easy to understand guide for dbcp newbies.

As far as I see, this guide /howto is the best one. Simple and detailed.

Saved me a couple of hours of investigation on API; thanks much!

Quick question

poolableConnectionFactory object at setupDataSource method is never used. Why did we create it?

Good question!

Why we have to implement :

"new PoolableConnectionFactory(connectionFactory, connectionPool, null, validationQuery, false, true);"

if is never read?

I think you have to

I think you have to implement that method because you need to test the connection. Look at the parameters: he pass de validation query.

You have to becasue.....

You need the PoolableConnectionFactory as it will become the factory for all of the objects created by the object pool. In the constructor of the PoolableConnectionfactory the PoolableConnectionfactory instance sets the factory on the pool object passed in the constructor. Sounds confusing huh... well it is. Here is the constructor of the PoolableConnectioFactory :

public PoolableConnectionFactory(ConnectionFactory connFactory, ObjectPool pool, KeyedObjectPoolFactory stmtPoolFactory, String validationQuery, boolean defaultReadOnly, boolean defaultAutoCommit) {
_connFactory = connFactory;
_pool = pool;
_pool.setFactory(this); ******This is where they set the factory on the pool.****

_stmtPoolFactory = stmtPoolFactory;
_validationQuery = validationQuery;
_defaultReadOnly = defaultReadOnly ? Boolean.TRUE : Boolean.FALSE;
_defaultAutoCommit = defaultAutoCommit;
}

So you still need the PoolableConnectionFactory. But we don't care about the reference to it so you could change

PoolableConnectionFactory poolableConnectionFactory =
new PoolableConnectionFactory(
connectionFactory,connectionPool,null,null,false,true);

to

new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);

Incompatible type of data

Under ConnectionManager exist a method setupDataSource, its return type is DataSource, but you do a return under dataSource variable, its type is PoolingDataSource. Netbeans point me an error. Could you explain me how to get rid of this error?

Thanks
Alejandro

ConnectionManager Best Practice

Hi,

I am using teh above example as a base/pattern and my application creates many threads connecting to the same datasource. Should The ConnectionManager above be kept as a Singleton so each thread would have a reference to it and thus get a reference to the data source rather than each thread creating a new ConnectinManager class instance?

Also, is there an optimum number for min/max threads for a MySQL DB?

Thanks

TB

Thanks!

Thanks much for the post. I'm using some part of the code in my codebase with due courtesy mentioned in my code, do let me know if you have any objection.

Cheers!
--sannidhi--

Thanks

Thank you for your kind feedback.

Feel free to use the code any way you need to. All code on this blog is licensed under "Creative Commons Attribution-Noncommercial-Share Alike 2.5 License or later.".

DBCP AutoReconnect

Hi,

I have used your code and it work perfectly. However, I got 2 problems here:

1. After connected the mysql db and dbcp will keep the connection there which is ready for me to make any query. I tried disable my wireless connection so that it lost connection to my remote mysql db for like 1 minute. But after I enable my wireless connection again, dbcp didn't auto reconnect it. Is there anywhere to make dbcp to auto reconnect once the internet connection is up?

2. My program need to connect to remote database to download data then insert it into local database. How can I initiate 2 dbcp ?

regards,
Mark

MultilineToStringStyle in commons.lang

Here's a tip. If you're using the newer version of commons.lang, replace

MultilineToStringStyle style = new MultilineToStringStyle();
ReflectionToStringBuilder tsb = new ReflectionToStringBuilder(this, style);

with

ReflectionToStringBuilder tsb = new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);