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'
}
我很困惑为什么 setAutoCommit() 在实际做任何事情之前就返回了,除非有什么东西导致 iter.fullIteration() 返回 false。