我正在使用tomcat连接池。但我跟随例外
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
所以我在context.xml中输入以下行来查找泄漏:
removeAbandoned="true" logAbandoned="true" removeAbandonedTimeout="3"
然后我开始关注异常org.apache.tomcat.dbcp.dbcp.AbandonedTrace$AbandonedObjectException: DBCP object created 2015-01-17 22:12:18 by the following code was never closed:
所以我发现导致这种泄漏的两种罪魁祸首方法。这两种方法有通用的连接方式,即调用unwrap来访问驱动程序特定的连接。
try (Connection conn = DataSourceConnectionPool.getConnection().unwrap(OracleConnection.class);
OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall(MIGRATE_ACCOUNT)) {
...
....
)
重要的是要注意的是我正在使用JDK7的try块,即自动资源管理,所以我不需要finally块。连接关闭由JDK自动处理。但为什么这个解开的连接没有关闭。当我想做以下事情时:
try (Connection poolConn = DataSourceConnectionPool.getConnection();
Connection conn = poolConn.unwrap(OracleConnection.class);
我得到java.sql.SQLException: Already closed.
所以这个联系有多接近。我是否必须手动完成而不使用try块?不应该尝试块句柄能够处理这个吗?
答案 0 :(得分:10)
这是对连接池的错误使用。你永远不应该在未打开的连接上调用close()
。
使用池化连接的正常流程是
Connection
,池获取物理连接并将其包装在自己的包装器中Connection
close()
上致电Connection
。这实际上并不是关闭任何东西,池的包装器拦截close()
调用并简单地返回到池中的(仍处于活动状态)连接。这是有效的,因为池有一个包装类,比如PoolableConnection
implements Connection
。 PoolableConnection
委托基础连接进行实际工作,但它以不同的方式实现close()
({1}}。这会破坏当前的PoolableConnection
包装器,并将底层Connection
返回到连接池。例如。
这样,您的程序逻辑可以从DataSource
获得连接,使用Connection
然后使用close()
,就像正常的,未加密的Connection
一样
正是这种透明性使连接池变得如此易于使用。
现在,当您致电unwrap
时,PooledConnection
可让您访问其内部,真实的Connection
代表。
您所做的是在代理上致电close()
!
这有两个影响:
close()
上调用PooledConnection
,因此Connection
不会返回到池中。所以你需要非常小心。 始终在您从池中获取的close()
上调用Connection
,将其返回池中。 从不在底层连接上调用close()
。
所以你的代码应该是:
try (final Connection poolConn = DataSourceConnectionPool.getConnection()) {
final Connection conn = poolConn.unwrap(OracleConnection.class);
//do stuff with conn
//do not close conn!!
}
//poolConn is returned to the pool