c3p0连接池啊就不多说了。有一个问题吧就是连接使用完毕后要怎么放回连接池,供下次继续使用。
答案是:
conn.close();// 放回
可以将连接放回。但在实际应用中有时候发现调用这个方法以后连接还是在使用中,没有被返还。
于是各种百度、google。
发现获取连接有两种方法。
第一种是
ComboPooledDataSource cpds = new ComboPooledDataSource();
conn = cpds.getConnection();
第二种是
conn = cpds.getConnectionPoolDataSource().getPooledConnection().getConnection();
先来说第一种,在使用c3p0时连接的获取和放回都和我们使用jdbc的时候是一样的,但是效果却不同,比如jdbc是真实的关闭掉了连接,而c3p0却是将连接放回了连接池。
那么它是如何做到的呢?
答案是使用代理,当咱们调用连接的close方法的时候其实被c3p0的代理方法C3P0ProxyConnection代理了,而C3P0ProxyConnection其实只是一个接口,在c3p0中它只有暂时一个实现NewProxyConnection,它同时实现了Connection和 C3P0ProxyConnection。然后它的结构
下面还有很多方法我就不都截出来了,但是看到这相信大伙都有一种恍然大悟的感觉了。
接着往上讲,当使用第一种方法获取连接:
//implementation of javax.sql.DataSource
public Connection getConnection() throws SQLException
{
PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
return pc.getConnection();
}
从池管理那里获取连接池,然后检出池内的连接,看起来很简单,但是随着一层一层剥开c3p0的代码我们可以发现最终在BasicResourcePool有些很有趣的东西,这使c3p0更和蔼可亲(其实是比较好理解了)。
/* keys are all valid, managed resources, value is a PunchCard */
HashMap managed = new HashMap();
/* all valid, managed resources currently available for checkout */
LinkedList unused = new LinkedList();
/* resources which have been invalidated somehow, but which are */
/* still checked out and in use. */
HashSet excluded = new HashSet();
他们分别是:管理着的连接,未使用的连接,失效的资源(但他们是任然检出并正在使用中的连接)。
看到这个不得不提起我们用来监听c3p0状态时使用的方法,
log.debug("正忙着 busy:{}", cpds.getNumBusyConnections());
log.debug("总共的 all:{}", cpds.getNumConnectionsDefaultUser());
log.debug("清闲的 Leisurely:{}", cpds.getNumIdleConnectionsDefaultUser());
log.debug("孤立连接 Excluded:{}", cpds.getNumUnclosedOrphanedConnectionsDefaultUser());
调用这些方法的时候c3p0又是怎么返回这些状态的呢,相信有的小伙伴心里已经有了答案。
public synchronized int getAvailableCount()
{ return unused.size(); }
public synchronized int getExcludedCount()
{ return excluded.size(); }
public synchronized int getAwaitingCheckinCount()
{ return managed.size() - unused.size() + excluded.size(); }
public synchronized int getAwaitingCheckinNotExcludedCount()
{ return managed.size() - unused.size(); }
所以说在c3p0管理的最后其实是通过一个个集合和map来对使用的未使用的集合进行管理,当使用连接不够但managed.size()<定义的最大连接数时,会生成新的连接并放入unused中,当managed.size()>定义的最大连接数时会等待配置的timeout时间,如果超出这个时间还是没有空闲的连接则会抛出异常。
在获取连接时会从unused中拿出连接并对该连接进行标记并添加监听ConnectionEventListener。
最后放回连接调用conn.close()方法其实使用的是NewProxyConnection的close方法,首先对连接进行清理,清空如ResultSets和Statements,reset一些属性,清除监听ConnectionEventListener,然后将连接归还到unused中。
有兴趣的朋友可以仔细去阅读c3p0的源码,其中这些取出归还操作其实包含很多同步和异步操作、异常的处理方式也很有意思,很多东西可以学习。
然而上面的那两种方法,第一种是通过BasicResourcePool来操作,第二种是通过WrapperConnectionPoolDataSource 直接从数据源获取连接,然后通过获取到的连接创建NewPooledConnection来获取连接的,注意到在使用时没有将他放入unused,所以在管理它,比如获取连接池使用情况的时候是获取不到的。
ps:此篇文章为更新文章,因为最开始一直在这里提倡第二种方法表示抱歉。在真正了解到他们两之后才明白过来。当然这篇文章还不算很深入,后续如果有机会再对c3p0连接这块进行研究的话会再更新文章的。谢谢看这篇文章的人,希望能有所收获。