Java Tomcat连接池:很少有方法不释放连接

Java Tomcat连接池:很少有方法不释放连接,java,tomcat,jdbc,connection-pooling,Java,Tomcat,Jdbc,Connection Pooling,我正在使用tomcat连接池。但我遵循的是例外 org.apache.tomcat.dbcp.dbcp.SQLNestedException:无法获取连接,池错误等待空闲对象超时 因此,我在context.xml中放入以下行以查找漏洞: removeAbandoned=“true”logaudded=“true”removeAbandonedTimeout=“3” 然后我开始得到以下异常org.apache.tomcat.dbcp.dbcp.弃置trace$弃置对象异常:由以下代码创建的dbcp

我正在使用tomcat连接池。但我遵循的是例外
org.apache.tomcat.dbcp.dbcp.SQLNestedException:无法获取连接,池错误等待空闲对象超时

因此,我在context.xml中放入以下行以查找漏洞:
removeAbandoned=“true”logaudded=“true”removeAbandonedTimeout=“3”

然后我开始得到以下异常
org.apache.tomcat.dbcp.dbcp.弃置trace$弃置对象异常:由以下代码创建的dbcp对象从未关闭:
所以我发现了两种导致泄漏的罪魁祸首方法。这两种方法具有获取连接的通用方法,即调用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:已经关闭。
那么这个连接有多近。我是否必须在不使用try块的情况下手动执行此操作?不应该尝试块句柄来处理此问题吗?

这是不正确的连接池用法。决不能在未包装的连接上调用
close()

try (Connection conn = DataSourceConnectionPool.getConnection().unwrap(OracleConnection.class);
        OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall(MIGRATE_ACCOUNT)) {
        ...
        ....
)
使用池连接的正常流程是

  • 获取
    连接
    ,池将获取一个物理连接,并将其包装在自己的包装器中返回
  • 您使用
    连接
  • 连接上调用
    close()
    。这实际上并没有关闭任何东西,池的包装器截取
    close()
    调用并简单地返回到池的连接(仍然处于活动状态)
  • 这是因为池有一个包装类,比如说
    PoolableConnection
    ,它
    实现连接
    PoolableConnection
    委托基础连接执行实际工作,但它实现(除其他外)
    close()
    的方式不同。这将销毁当前的
    PoolableConnection
    包装器,并将正在运行的
    连接
    返回到连接池。比如说

    这样,您的程序逻辑可以从
    数据源
    获取连接,使用
    连接
    ,然后使用
    关闭()
    ,就像正常的、未冷却的
    连接一样

    正是这种透明性使得连接池如此易于使用

    现在,当您调用
    unwrap
    时,
    PooledConnection
    允许您访问它的内部真正的
    连接

    您要做的是对代理调用
    close()

    这有两个影响:

  • 它不会在
    PooledConnection
    上调用
    close()
    ,因此
    连接不会返回到池中
  • 它从池底关闭下引连接。这应该不是问题,因为池本身会处理断开的连接
  • 所以你需要非常小心始终在从池中获取的
    连接上调用
    close()
    ,将其返回到池中从不对基础连接调用
    close()

    try (Connection conn = DataSourceConnectionPool.getConnection().unwrap(OracleConnection.class);
            OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall(MIGRATE_ACCOUNT)) {
            ...
            ....
    )
    
    因此,您的代码应该是:

    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
    

    我的第一个问题是,您如何能够验证.unwrap()代码是泄漏发生的地方?