DBのコネクションプールを簡単に実現する現場に活かすJakarta Project(6)

» 2003年05月15日 00時00分 公開
[横田健彦(株)東芝 研究開発センター]

 今回も、前回「Commonsでオブジェクトプーリングを実現」に引き続きCommonsプロジェクトの活用法を紹介します。前回はPoolを利用してデータベースのコネクションプーリングを実現しました。今回はDBCPを用いて、より容易なデータベースのコネクションプーリングを実現してみましょう。なお、この原稿執筆時点でのDBCPコンポーネントの最新バージョンは1.0です。

PoolingDriverクラスとPoolingDataSourceクラス

 通常JDBCを用いたプログラミングではConnectionオブジェクト(java.sql.Connection)を使ってデータベースにアクセスしますが、Connectionオブジェクトを取得するにはDriverオブジェクト(java.sql.Driver)やDataSourceオブジェクト(javax.sql.DataSource)が必要になります。DBCPではこれらのクラスに対応するPoolingDriverクラス(org.apache.commons.dbcp.PoolingDriver)とPoolingDataSourceクラス(org.apache.commons.dbcp.PoolingDataSource)が用意されています。DBCPを用いたプログラミングでは、まずこれらのクラスのいずれかのインスタンスを生成し、生成したインスタンスからConnectionオブジェクトを取得することになります。

 ただし、これらのクラスのインスタンスを利用するためにはあらかじめObjectPoolオブジェクトを与えておく必要があります。実はDBCPコンポーネントはPoolコンポーネントのオブジェクトプーリング機能を利用してコネクションプーリング機能を実現していますので、DBCPコンポーネントを利用するには前回説明したPoolコンポーネントのObjectPoolオブジェクトやPoolableObjectFactoryオブジェクトを用意する必要があるのです。

 PoolingDriverやPoolingDataSourceに与えるObjectPoolとしては、Poolが提供しているObjectPool実装クラスを利用することができます。また、前回に説明しましたようにObjectPoolを利用するためにはPoolableObjectFactoryインターフェイスの実装クラスを用意する必要がありますが、これに関してはDBCP側で用意してくれています。それがPoolableConnectionFactoryクラス(org.apache.commons.dbcp.PoolableConnectionFactory)です。

ConnectionFactoryクラス

 PoolableConnectionFactoryにはConnectionオブジェクトを生成するためのクラスとしてConnectionFactoryインターフェイス(org.apache.commons.dbcp.ConnectionFactory)の実装クラスを与える必要があります。ConnectionFactoryインターフェイスの実装クラスとしてはDriverConnectionFactory、DriverManagerConnectionFactory、DataSourceConnectionFactoryの3つ(すべてorg.apache.commons.dbcpパッケージ)が用意されています。

DBCPの使用例

 ここではDBCPの使用例として、Poolの使用例で説明したWebアプリケーションをDBCPを利用するように書き直したものについて説明します。書き直したコードは、前回のサンプルコードのアーカイブに含まれているindex2.jspです。

 DBCPのコネクションプールを利用するための準備として、index2.jspのjspInit()メソッド中でPoolingDataSourceまたはPoolingDriverクラスのインスタンスを生成します(リスト1)。Poolの例ではこのメソッド中でObjectPoolのインスタンスを生成しましたが、DBCPではObjectPoolインスタンスを生成したうえでそれをPoolingDataSourceまたはPoolingDriverインスタンスに与えます。

リスト1 index2.jspのjspInit()メソッド
   public void jspInit()
 {
  try {
   // MySQLのためのJDBCドライバをロードします
   Class.forName("com.mysql.jdbc.Driver");

   // ObjectPoolインスタンスを生成します            (1)
   ObjectPool pool = new StackObjectPool();

   // Connectionオブジェクトを生成するためのConnectionFactory
   // インスタンスを生成します                      (2)
   ConnectionFactory conFactory
    = new DriverManagerConnectionFactory(url_, user_, password_);

   // PoolableConnectionFactoryインスタンスを生成します。
   new PoolableConnectionFactory(
    conFactory, pool, null, null, false, true);    (3)

   // プーリング機能を持つDataSourceインスタンスを生成します(4)
   ds_ = new PoolingDataSource(pool);
  } catch (Throwable t) {
   throw new RuntimeException(t);
  }
 }

 順を追って見ていきましょう。まずObjectPoolのインスタンス(ここではStackObjectPool)を生成します(1)。ここで注意したいのは、前回説明したPoolの例ではコンストラクタにPoolableObjectFactoryインスタンスを渡したのに対してDBCPの場合は引数なしのコンストラクタを呼び出すということです。これに関しては後ほど説明します。

 次にConnectionオブジェクトを生成するためのConnectionFactoryのインスタンスを生成します(2)。ここではDriverManagerConnectionFactoryを用いています(なお、このクラスのコンストラクタは引数としてDriverManager#getConnection()メソッドと同じもの、すなわちデータベース接続用のURLとユーザー名とパスワードを取りますので、DriverManagerクラスを置き換える場合はDriverManagerConnectionFactoryを使用すると楽です)。

 次はObjectPoolに与えるPoolableObjectFactoryインスタンスとして、コネクションプール用に用意されているPoolableConnectionFactoryインスタンスを生成します(3)。このときコネクションプールの振る舞いを決定するような各種パラメータをコンストラクタに指定することができます。コンストラクタPoolableConnectionFactory(connFactory、pool、stmtPoolFactory、validationQuery、defaultReadOnly、defaultAutoCommit)のそれぞれの引数の意味を表1にまとめておきます。

表1 PoolableConnectionFactoryのコンストラクタの引数
引数 説明
connFactory Connection オブジェクトを生成するためのConnectionFactoryインスタンス
pool Connectionのプーリングに用いるObjectPoolインスタンス
stmtPoolFactory PreparedStatementオブジェクトをプーリングする場合、プーリング用のKeyedObjectPoolオブジェクトを生成するためのKeyedObjectPoolFactoryインスタンス。PreparedStatementオブジェクトをプーリングしない場合はnullを指定
validationQuery Connectionが有効であるかどうかを検査するためのSQL文
defaultReadOnly プールから取り出されたConnectionを読み込み専用にする
defaultAutoCommit プールから取り出されたConnectionを自動コミットモードにする

 なお生成したPoolableConnectionFactoryオブジェクトに関しては、コンストラクタの内部でObjectPool#setFactory()メソッドが呼ばれることで自動的にObjectPoolと関連付けられます。このためObjectPoolインスタンスの生成時にコンストラクタにPoolableObjectFactoryオブジェクトを渡さなくてもよかったのです。

 最後にPoolingDataSourceクラスのインスタンスを生成します(4)。このとき、最初に生成したObjectPoolオブジェクト(StackObjectPool)をコンストラクタの引数として渡します。これでコネクションプールのためのDataSourceオブジェクトの準備ができました。

 このようにして準備したコネクションプールを使用する部分のコードをリスト2に示します。作成したPoolingDataSourceはDataSourceインターフェイスを実装していますので、通常のDataSourceを扱うのと同様にgetConnection()メソッドを用いてConnectionを取り出すことができます(5)。また、取り出したConnectionオブジェクトをプールに戻すには、ObjectPool#returnObject()を用いるのではなく単にConnection#close()メソッドを呼び出せばよいようになっています(6)。というのは実はPoolingDataSourceが実はConnectionオブジェクトのラッパオブジェクトを返すようになっていて、このラッパオブジェクトのclose()メソッドがオブジェクトプールへのConnectionの返却処理を行ってくれるようになっているのです。このように、DBCPではPoolを直接用いてコネクションプールを実装した場合とは違って、コネクションプールを利用していることを全く意識せずにConnectionを使用することができるようになっています。

リスト2 index2.jspのgetConnection()とreturnConnection()メソッド
   private Connection getConnection(boolean usePool)
  throws Exception
 {
  if (usePool) {
   // コネクションプールからオブジェクトを取り出します (5)
   return ds_.getConnection();
  } else {
   return DriverManager.getConnection(url_, user_, password_);
  }
 }

 private void returnConnection(Connection con, boolean usePool)
  throws Exception
 {
  if (con != null) {
   if (usePool) {
    // コネクションプールにコネクションを返却します   (6)
    con.close();
   } else {
    con.close();
   }
  }
 }

プーリングについてのまとめ

 前回と今回とでPoolコンポーネントとDBCPコンポーネントをご紹介しました。上層のアプリケーション開発時にこれらのコンポーネントを直接使用する機会は多くないかもしれませんが、ミドルウェア寄りのシステム開発などでは使用する場面もあるかと思います。またこれらのコンポーネントはStrutsのようなJakartaのほかのプロダクトでも用いられていますので、仕組みを覚えておくと役に立つこともあるかと思います。

 次回はJavaコレクションフレームワークを拡張するCollectionsコンポーネントとCommonsの中でもユーティリティ色の強いLangコンポーネントについて説明します。

筆者プロフィール

横田健彦(よこた たけひこ)

東京工業大学卒業後、(株)東芝に入社。現在、知識メディアラボラトリーにてコミュニティベース情報共有システムの研究に従事。小学校のころからコンピュータに触れ、主にゲームプログラミングを通してBASIC、アセンブラをはじめとする多数の言語を学ぶ。JavaではJakartaプロジェクトの成果物を利用していく中で主にWebアプリケーションプログラミングの面白さに引かれ、Ja-Jakartaプロジェクトの活動に貢献する一方でオープンソースのJavaベースのWebコンテンツ管理システムであるKvasir/Soraの開発を行っている。



Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。