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で設定できるのかな・・。
後できちんと読み返しておこう。
斜め読みして作ったサンプルなので、このまま使うのは危険かもしれませぬ。