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