[Seasar-user:15907] [S2DBCP]ローカルトランザクションを利用する場合にConnectionPoolImplからのコネクション取得に失敗することがある

Akira Asakawa [E-MAIL ADDRESS DELETED]
2008年 10月 6日 (月) 14:40:06 JST


チェンジビジョンの淺川と申します。

s2-extension-2.4.29のConnectionPoolImplを利用して
接続をsetAutoCommit(true)したままclose()すると、
次のgetConnection()で例外が発生することがあります。

JDBCドライバの実装にも関係するので、全てのJDBCドライバで
再現するわけではないと思いますが、
postgresql-8.3-603.jdbc3.jarでは必ず例外が発生しました。

org.postgresql.util.PSQLException: Cannot change transaction read-only
property in the middle of a transaction.
at
org.postgresql.jdbc2.AbstractJdbc2Connection.setReadOnly(AbstractJdbc2Connection.java:586)
at
org.seasar.extension.dbcp.impl.ConnectionWrapperImpl.setReadOnly(ConnectionWrapperImpl.java:218)
at
org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOut(ConnectionPoolImpl.java:325)
at
org.seasar.extension.dbcp.impl.DataSourceImpl.getConnection(DataSourceImpl.java:59)
(以下省略)

開始直後ではないトランザクションに対して
Connection#setReadOnly()を実行したからのようです。

----------------------------------------------------------------
再現コード
----------------------------------------------------------------

≪jdbc.dicon≫

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components namespace="jdbc">
<include path="jta.dicon" />
<include path="jdbc-extension.dicon"/>
<component name="xaDataSource"
class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
<property name="driverClassName">"org.postgresql.Driver"</property>
<property name="URL">"jdbc:postgresql://xxx/xxx"</property>
<property name="user">"xxx"</property>
<property name="password">"xxx"</property>
</component>
<component name="connectionPool"
class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
<property name="timeout">600</property>
<property name="maxPoolSize">10</property>
<property name="allowLocalTx">true</property>
<property name="validationQuery">"select 0"</property>
<destroyMethod name="close" />
</component>
<component name="dataSource"
class="org.seasar.extension.dbcp.impl.DataSourceImpl" />
</components>

≪Java Source≫

SingletonS2ContainerFactory.init();
S2Container container = SingletonS2ContainerFactory.getContainer();
DataSource ds = (DataSource) container.getComponent("dataSource");

// 何もせずに返却する場合は問題なし
ds.getConnection().close();
ds.getConnection().close();

Connection co = ds.getConnection();
co.setAutoCommit(false);
// autoCommit:true の状態でプールに返却
co.close();

// checkOut()内で例外発生
ds.getConnection().close();

----------------------------------------------------------------
回避方法
----------------------------------------------------------------

S2DBCPではプールへの返却時(もしくは再利用時)にコネクションの
autoCommitを確認しておらず、前回のプールへの返却前の状態を
引き継いでしまっています。

そこで試しにConnectionPoolImplをオーバーライドして
以下のように変更すると問題なく動いてそうに見えます。

public synchronized void checkIn(ConnectionWrapper connection) {
try {
connection.getPhysicalConnection().setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
super.checkIn(connection);
}

以上、ご確認のほどよろしくお願いいたします。


Seasar-user メーリングリストの案内