Commons-DBCP

  • 投稿日:
  • カテゴリ:

Commons-DBCPの使い方です。

Commons-DBCPはデータベースのコネクションプールとPreparedStatementのキャッシュをしてくれます。TomcatやStruts1, Springなど有名なアプリケーションサーバやフレームワークが採用しているライブラリですが、これを直接使ってみたいと思います。

ちなみに、こんな記事を見つけました。 かなり前の内容のようなので現状はわかりませんが・・・。

そもそも、OracleのJDBCドライバには、
適切な(?)ファイナライザが実装されていないらしく、
Connectionにしても、Statementにしても、ResultSetにしても、
closeし忘れるとメモリリークが起こるという不具合というか仕様があるらしいです。

でもって、Oracleのコネクションプーリング実装でclose忘れされた日には、
どうにも開放する手段がなく、メモリリークしてしまいます。

その辺りを確かめるべく、
Jakarta Commons DBCPも交えつつ、
色々実験してました。

すると、
・OracleのJDBCドライバをそのまま使用
→ 高負荷時にConnectionを取得できないことがあるが、メモリリークはなし。
・OracleのJDBCドライバのプーリングを使用
→ 高負荷時にConnectionを取得できないことがあり、メモリリークもある。
・Commons DBCP&OracleのJDBCドライバを使用
→ 高負荷時もConnectionを取得でき、メモリリークはなし。
(ただし、必要最小限のメモリは使用していると思われる。)
という結果が得られました。

というわけで、
OracleのJDBCドライバに入ってるコネクションプーリング実装は、
「百害あって一利なし」
ということが判明しました。
それはさておき、簡単なサンプルコードは以下になります。
public class ConnectionManager {

    private static DataSource ds = null;
    private static PoolableConnectionFactory poolFactory;

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

    public static void init(String classNmae, String url, String user, String password) throws ClassNotFoundException, SQLException {
        // JDBCドライバをロードする
        Class.forName(classNmae, true, DriverManagerConnectionFactory.class.getClassLoader());

        // ObjectPoolインスタンスを生成する
        // StackObjectPoolでは細かい制御ができないためGenericObjectPoolを使う
        GenericObjectPool pool = new GenericObjectPool();
        pool.setMaxActive(100);
        pool.setMinIdle(10);
        pool.setMaxWait(5000);
        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);

        // Connectionを生成するためのFactoryInstanceを生成する
        ConnectionFactory factory = new DriverManagerConnectionFactory(
                url, user, password);

        // PreparedStatementをキャッシュするためのFactoryを生成する
        KeyedObjectPoolFactory pstFactory = new StackKeyedObjectPoolFactory(50, 10);

        // PoolableConnectionFactoryを生成する
        poolFactory = new PoolableConnectionFactory(
                factory,
                pool,
                pstFactory,
                null,
                false,
                true);

        // プーリング機能を持つDataSourceを生成する
        ds = new PoolingDataSource(poolFactory.getPool());

    }

    public static int getNumActive() {
        // 使用中のConnection数を取得する
        return poolFactory.getPool().getNumActive();
    }

    public static int getNumIdle() {
        // Idle状態のConnection数を取得する
        return poolFactory.getPool().getNumIdle();
    }

    public static void destroy() throws Exception {
        // Connectionの破棄をする
        poolFactory.getPool().close();
    }

    public static Connection getConnection() throws SQLException {
        // Connectionを取得する
        return ds.getConnection();
    }
}

説明はソースにコメント書いてあるので、そちらを参照してください・・・。
GenericObjectPoolの詳細はこちらのJavaDocを見ると良いと思います。
ある程度の細かい動作を設定できるようです。個人的にはRetry回数みたいなのを設定できると嬉しいのですが・・。
あと、取得できないときは例外投げないでnullを返して欲しかったりもします・・・。これはsetWhenExhaustedActionで設定できるのかな・・。
後できちんと読み返しておこう。

斜め読みして作ったサンプルなので、このまま使うのは危険かもしれませぬ。

新しいサイトもよろしくお願いします!