戯言

つらつらと気づいたことを書いていきます。人狼とか。

スポンサーサイト


上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

コネクションプール利用時のConnection.setAutoCommit(false)のワナ


Connection.setAutoCommit() でちょっとハマりました。

とあるトランザクション制御が必要ない単一のクエリで、
デフォルトの true が効かずに、autoCommitが意図せずに false に
なっている場合があって、こちらの期待通りの動きをしない可能性がありました。


どういうことかというと・・・、

トランザクション制御が必要なクエリでは、
autoCommit を false に設定していますが、クエリ実行後に、このトランザクションは終了して、
Connection も close するので、false になるのはこのトランザクションのみで、
他のトランザクションへの影響はないと思っていました。

ですが、一旦 false に設定すると、別のクエリで、Connection を取得した
場合に、false の設定のままとなっていて、
マルチスレッド環境で、複数のクエリが動いた場合に、
期待とおりでない動作をするケースがあったようです。

ざっくり書くと、トランザクション制御が必要なクエリを実行するメソッドは、こんな感じで書いていました。

public void トランザクション制御が必要となるクエリの実行(){
	Connection conn = null;
	try {
		conn = createConnection();
		conn.setAutoCommit(false);
			:
			:
			// トランザクション制御するクエリ実行
			:
			:
		conn.commit();
	} catch (SQLException e) {
		conn.rollback();
	} finally {
		conn.close();
	}
}
public void トランザクション制御が必要ないクエリの実行(){
	Connection conn = null;
	try {
		conn = createConnection();
			:
			:
		// トランザクション制御しないクエリ実行
			:
	} finally {
		conn.close();
	}
}

createConnection() というコネクション払出し用のメソッドを作っていて、
その中身はこんな感じです。

public Connection createConnection() throws SQLException {
	try{
		InitialContext ic = new InitialContext();
		DataSource ds = (DataSource)ic.lookup("java:comp/env/jdbc/・・・・");
		Connection conn = ds.getConnection();
		
		return conn;
	}
	catch (NamingException ex){
		// 例外処理(略)
	}
}
「トランザクション制御が必要となるクエリの実行()」が呼ばれ、終了した時点で、
autoCommit は false のままとなってます。

その後、別のトランザクションで、「トランザクション制御が必要ないクエリの実行()」を
呼出した場合に、新たにcreateConnection() を呼び出すのですが、
返ってくる Connection オブジェクトの autoCommit は false になっていました。

単一クエリの実行では、(自動コミットされると思っているので)commit()を
明示的に呼び出していないので、close()が呼ばれるまでの間、
コミットされていない状態になってしまっていました。
createConnection() で return する前に、true 設定するようにしておきました。



では、なぜ、コネクションを切ってるのに、次のコネクションに設定が残ってるのか。
よくよく考えたら、コネクションプールを使ってるからでした。
予めコネクションを作ってプールしといて、必要なときに払いだしているのだから、
当たり前といえば当たり前。

一応、こんな感じのソースで確認しました。

コネクションプールを使う場合
public Connection createConnection() throws SQLException {
	try{
		// DBCPを利用
		InitialContext ic = new InitialContext();
		DataSource ds = (DataSource)ic.lookup("java:comp/env/jdbc/・・・・");
		Connection conn = ds.getConnection();
		
		// 一旦クエリ実行時に false にセットすると、次からは払いだした時点でfalseになる。
		System.out.println("autoCommit=" + conn.getAutoCommit());

		return conn;
	}
	catch (NamingException ex){
		// 例外処理(略)
	}
}
コネクションプールを使わない場合
public Connection createConnection() throws SQLException {
	try{
		// CPを利用しない
		Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
		Connection conn = DriverManager.getConnection("jdbc:derby:/.../....",name, pass);
		
		// 常に true になることを確認。
		System.out.println("autoCommit=" + conn.getAutoCommit());

		return conn;
	}
	catch (ClassNotFoundException ex){
		// 例外処理(略)
	}
}



関連記事

管理者にだけ表示を許可する
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。