パフォーマンスチューニング

Javaでバッチとか、同一コネクション上で、同じSQL(Preparedな)を大量に発行している場合、且つ、生成したPreparedStatementをSQL発行の度にcloseしているような実装が既に存在しており、ロジックの変更はしたくないといった場合のやっつけパフォーマンス改善手段。
具体的には、PreparedStatementのキャッシュと、closeの遅延をやるんだけども、Connection内でPreparedStatementをSQL文そのものをキーとしてMapにキャッシュし、ステートメントのcloseはConnectionのクローズ時にまとめてやってしまえというもの。(発行しているSQL文の種類が多すぎると、メモリ食いすぎて使えないので注意。><)

java.sql.PreparedStatementのラッパを作る。

 public class PreparedStatementCache implements PreparedStatement {
	private PreparedStatement _pstmt = null;

	public PreparedStatementForCache(PreparedStatement pstmt) {
		_pstmt = pstmt;
	}

 	@Override
	public void close() throws SQLException {
		//なにもしない、させない。
	}

        //後はすべて、_pstmtへの委譲メソッド。
 }

java.sql.Connectionのラッパを作る。

public class StatementCacheConnection implements Connection {
	private Connection _con = null;
	private Map<String,PreparedStatement> _stmtCache = null;
	
	public StatementcacheConnection(Connection con) {
		_con = con;
		//_stmtCache = new HashMap<String,PreparedStatement>();↓コメント欄での指摘により修正
                _stmtCache = new IdentityHashMap<String,PreparedStatement>();
	}

	public void close() throws SQLException {
		//念のため、キャッシュしたステートメントをすべてclose。
		for(PreparedStatement stmt : _stmtCash.values()) {
			stmt.close();
		}
		_stmtCache.clear();//キャッシュをぶっ飛ばす。
		_con.close();
	}

	public PreparedStatement prepareStatement(String sql) 
                                                throws SQLException {
		PreparedStatement stmt = _stmtCache.get(sql);
		if (stmt == null) {
			stmt = _con.prepareStatement(sql);
			_stmtCache.put(sql,stmt);
		}
		return new PreparedStatementForCache(stmt); //ラップしる
	}

	//あとは、全てのprepareStatementを同じように実装
	//他は全て_conへの委譲メソッド

}

生のConnectionをラップ

JDBCのConnectionを生成もしくは取得している場所で、生コネクションをStatementCashConnectionでラップすればおk。

以上。
この前やっつけ仕事で使ったら超速。
びびった。
俺すげーとか勘違いした。
ラッキー。
まぁ、たまたまボトルネックにクリーンヒットしただけなんだけども...
パフォーマンスチューニングには計測が大事ですよね。ええ。わかってますよ。


Oracleの場合、SQLの種類よりもMAX_CURSORSの値が多くないとエラーとなる。Statementを開きっぱなしにするということはカーソルを開きっぱなしにするということと同じ。