错误原因: 事务不关,并且非事务交替进行
总的来说,就是先开启了事务连接,未提交或关闭,导致连接池连接全部占满。
此时进行一次非事务连接操作,但是因为此时已经没有可以空闲的连接,并且创建的连接数已经最大。
pooled 连接池会从活动的连接里面找一个 连接时间超过配置的 连接,
删除(设置失效标志,valid),
重新创建一个连接使用。但是这个旧对象(暂且称其为对象)在我们的事务集合里面仍然保留引用,此时已经不能用了(valid 为false),所以报错。
是高并发? 不可能, 挖一下!
使用的 POOLED 连接池<!-- 对事务的管理和连接池的配置 --><environments default="env_default"><environment id="env_default"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /><property name="poolPingEnabled" value="true" /><property name="poolPingQuery" value="SELECT CURDATE()" /><!--<property name="poolMaximumCheckoutTime" value="1" />--><!--<property name="poolMaximumActiveConnections" value="1"/>--><!--<property name="poolMaximumIdleConnections" value="0"/>--><!--<property name="poolTimeToWait" value="1"/>--></dataSource></environment>
实现代码:
PooledDataSource.java/**
* This is a simple, synchronous, thread-safe database connection pool.
*
* @author Clinton Begin
*/
public class PooledDataSource implements DataSource {private static final Log log = LogFactory.getLog(PooledDataSource.class);private final PoolState state = new PoolState(this);private final UnpooledDataSource dataSource;// OPTIONAL CONFIGURATION FIELDSprotected int poolMaximumActiveConnections = 10; protected int poolMaximumIdleConnections = 5;protected int poolMaximumCheckoutTime = 20000; // 连接最长持有时间protected int poolTimeToWait = 20000;// 连接等待时间protected int poolMaximumLocalBadConnectionTolerance = 3;protected String poolPingQuery = "NO PING QUERY SET";protected boolean poolPingEnabled;protected int poolPingConnectionsNotUsedFor;private int expectedConnectionTypeCode;
……/*
* Invalidates the connection
* 这个字段判断 这个连接是否可用
*/
public void invalidate() {valid = false;
}private PooledConnection popConnection(String username, String password) throws SQLException {boolean countedWait = false;PooledConnection conn = null;long t = System.currentTimeMillis();int localBadConnectionCount = 0;while (conn == null) {synchronized (state) {if (!state.idleConnections.isEmpty()) {// Pool has available connectionconn = state.idleConnections.remove(0);if (log.isDebugEnabled()) {log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");}} else {// Pool does not have available connectionif (state.activeConnections.size() < poolMaximumActiveConnections) {// Can create new connectionconn = new PooledConnection(dataSource.getConnection(), this);if (log.isDebugEnabled()) {log.debug("Created connection " + conn.getRealHashCode() + ".");}} else {// Cannot create new connectionPooledConnection oldestActiveConnection = state.activeConnections.get(0); // **当不能创建新连接,并且没有空闲连接 可用的时候,从active中拿一个检查 连接时间是否超过poolMaximumCheckoutTime。**long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();if (longestCheckoutTime > poolMaximumCheckoutTime) { // 超过了,就可以拿来用了。// Can claim overdue connectionstate.claimedOverdueConnectionCount++; state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;state.accumulatedCheckoutTime += longestCheckoutTime;state.activeConnections.remove(oldestActiveConnection);if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {try {oldestActiveConnection.getRealConnection().rollback();} catch (SQLException e) {/*Just log a message for debug and continue to execute the followingstatement like nothing happend.Wrap the bad connection with a new PooledConnection, this will helpto not intterupt current executing thread and give current thread achance to join the next competion for another valid/good databaseconnection. At the end of this loop, bad {@link @conn} will be set as null.*/log.debug("Bad connection. Could not roll back");} }conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);// **new 一次,得到一个新连接。**conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());oldestActiveConnection.invalidate(); // 因为要维持连接数目,所以旧连接失效if (log.isDebugEnabled()) {log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");}} else {// Must waittry {if (!countedWait) {state.hadToWaitCount++;countedWait = true;}if (log.isDebugEnabled()) {log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");}long wt = System.currentTimeMillis();state.wait(poolTimeToWait);state.accumulatedWaitTime += System.currentTimeMillis() - wt;} catch (InterruptedException e) {break;}}}}if (conn != null) {// ping to server and check the connection is valid or notif (conn.isValid()) {if (!conn.getRealConnection().getAutoCommit()) {conn.getRealConnection().rollback();}conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));conn.setCheckoutTimestamp(System.currentTimeMillis());conn.setLastUsedTimestamp(System.currentTimeMillis());state.activeConnections.add(conn);state.requestCount++;state.accumulatedRequestTime += System.currentTimeMillis() - t;} else {if (log.isDebugEnabled()) {log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");}state.badConnectionCount++;localBadConnectionCount++;conn = null;if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {if (log.isDebugEnabled()) {log.debug("PooledDataSource: Could not get a good connection to the database.");}throw new SQLException("PooledDataSource: Could not get a good connection to the database.");}}}}}if (conn == null) {if (log.isDebugEnabled()) {log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");}throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");}return conn;}
}