Glassfish 4.0 でも同じ問題が発生しており、その根底にも到達していません。ほとんどの人は、relaxAutoCommit
フラグを設定して忘れているようです。私たちが行ったテストでsetAutoCommit(false)
は、返された状態がgetAutoCommit()
正しくないだけでなく、実際に MySQL データベースにクエリを実行SELECT @@session.autocommit
すると、セッションに対して自動コミットがまだ有効になっていることがわかります。
設定relaxAutoCommit
は危険なので避けるべきだと思います!Connector/Jのドキュメントを実際に読んでみるとrelaxAutoCommit
、明確な目的があることがわかります。
ドライバーが接続する MySQL のバージョンがトランザクションをサポートしていない場合でも、commit()、rollback()、および setAutoCommit() の呼び出しを許可しますか (true/false、デフォルトは「false」)?
例外を抑制するためだけに設定するのは間違いです、IMO。
最終的に を実装したcom.mysql.jdbc.ConnectionLifecycleInterceptor
ところ、何らかの理由でsetAutoCommit(false)
、プールから返された接続で呼び出しを行うと、ドライバーの接続で実際に自動コミットを設定できず、セッションでの設定に失敗することがあることがわかりました。
接続がこの一貫性のない状態になったときに操作を再試行することで、この問題を回避しました。
ConnectionLifecycleInterceptor と追加のログを有効にすると、アプリケーションのログで生成される出力の一部が次のようになります。
18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit set to: false
18 Feb 16:47:07 INFO [http-listener-1(11)] - Autocommit before command: SetActionCommand
18 Feb 16:47:07 INFO [http-listener-1(11)] - Autocommit state is: connAutocommit=true, sessAutocommit=true
18 Feb 16:47:07 INFO [http-listener-1(11)] - SetActionCommand: host: bat424211win64, action: Checking, comment: Checking:20160218.164706.EST: Stuck Windows Check
18 Feb 16:47:07 INFO [http-listener-1(11)] - Autocommit after command: SetActionCommand
18 Feb 16:47:07 INFO [http-listener-1(11)] - Autocommit state is: connAutocommit=true, sessAutocommit=true
18 Feb 16:47:07 ERROR [http-listener-1(11)] - commit called
18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit should be false
18 Feb 16:47:07 ERROR [http-listener-1(11)] - Exception while executing: SetActionCommand
18 Feb 16:47:07 ERROR [http-listener-1(11)] - rollback called
18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit should be false
18 Feb 16:47:07 WARN [http-listener-1(11)] - Connection failure
18 Feb 16:47:07 ERROR [http-listener-1(11)] -
ConnectionLifecycleInterceptor は、setAutoCommit(false) を呼び出しているという事実をログに記録し、SetActionCommand の呼び出しの前後に追加のログを記録して、getAutoCommit() と session.autocommit の結果を報告しています。
関連する Connector/J のビットと、私自身の追加コメントの一部:
com.mysql.jdbc.ConnectionImpl:
public void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
synchronized (getConnectionMutex()) { // Mutex is 'this'
checkClosed();
if (this.connectionLifecycleInterceptors != null) { // Normally should be 'null'
IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {
@Override
void forEach(Extension each) throws SQLException {
if (!((ConnectionLifecycleInterceptor) each).setAutoCommit(autoCommitFlag)) { // Logged 'auto-commit set to: false'
this.stopIterating = true;
}
}
};
iter.doForAll();
if (!iter.fullIteration()) { // Only one listener (ours) AFAIK, unlikely to return here
return;
}
}
if (getAutoReconnectForPools()) { // Default for autoReconnectForPools is 'false'
setHighAvailability(true);
}
try {
if (this.transactionsSupported) { // This is 'true'
boolean needsSetOnServer = true;
if (this.getUseLocalSessionState() && this.autoCommit == autoCommitFlag) { // Default for useLocalSessionState is 'false'
needsSetOnServer = false;
} else if (!this.getHighAvailability()) { // This is 'false'
needsSetOnServer = this.getIO().isSetNeededForAutoCommitMode(autoCommitFlag); // Looks like this is always 'true'?
}
// this internal value must be set first as failover depends on it being set to true to fail over (which is done by most app servers and
// connection pools at the end of a transaction), and the driver issues an implicit set based on this value when it (re)-connects to a
// server so the value holds across connections
this.autoCommit = autoCommitFlag; // Updated value is not reflected in getAutoCommit()! We never get here?
if (needsSetOnServer) {
execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, DEFAULT_RESULT_SET_TYPE,
DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
}
} else {
if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 do not support transactions",
SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
}
this.autoCommit = autoCommitFlag;
}
} finally {
if (this.getAutoReconnectForPools()) {
setHighAvailability(false);
}
}
return;
}
}
com.mysql.jdbc.MysqlIO:
protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) {
if (this.use41Extensions && this.connection.getElideSetAutoCommits()) { // Default for elideSetAutoCommits is 'false'
boolean autoCommitModeOnServer = ((this.serverStatus & SERVER_STATUS_AUTOCOMMIT) != 0);
if (!autoCommitFlag && versionMeetsMinimum(5, 0, 0)) {
// Just to be safe, check if a transaction is in progress on the server....
// if so, then we must be in autoCommit == false
// therefore return the opposite of transaction status
boolean inTransactionOnServer = ((this.serverStatus & SERVER_STATUS_IN_TRANS) != 0);
return !inTransactionOnServer;
}
return autoCommitModeOnServer != autoCommitFlag;
}
return true; // Should always be returning 'true'
}
何かが iter.fullIteration() に false を返させない限り、実際に何かをする前に setAutoCommit() が戻っているように見える理由について、私は困惑しています。