前段时间做的一个应用hibernate的项目,在进行压力测试的时候发现,程序运行一段时间数据库连接就会爆满。检查程序,发现一个可疑代码,那就是使用session 打开一个connection,使用完毕之后,调用只关闭了session,但是没有关闭connection。代码如下:
在执行完业务逻辑之后,没有对打开的connection进行关闭就退出了(session的关闭在模板类RollbackTrans中完成)。现在的疑问是,只关闭session,即调用session.close()会一起关闭connection吗?带着这个疑问,我查看了源代码,写代码实际运行测试,下面是解决疑问的过程:
1、查看源代码:
session.close代码部分,代码是在org.hibernate.impl.SessionImpl中实现的
这个close做了几个事情:
1、检查是否已经关闭
2、检查是否打开统计配置,如果是的话就进行关闭统计(hibernate中有一个配置项,用于监控sessionFactory的连接情况hibernate.generate_statistics)
3、关闭所有子session
4、这点关键,与本文章讨论的问题关系很大。那就是调用jdbcContext.getConnectionManager().close()。从这行代码来看,我的猜测是:session.close应该是会关闭有它打开(session.connection())的connection。接下来我们来研究这个close方法(jdbcContext.getConnectionManager().close),看下面代码
留意上面有中文注释的代码行,由此,我们不难断定,在调用session.close的时候,hibernate会将由本session打开的connection关闭。现在,我们来解决上面第13行代码注释的疑问,以确保上面讨论中关闭的connection对象就是由session.connection()打开的。解决这个疑问,先来看看session.connection()代码,同样,这部分代码也是在org.hibernate.impl.SessionImpl中实现的
以上jdbcContext、connectionManager和前面讨论close时的jdbcContext、connectionManager分别是同一个对象。在borrowConnection中
1、 首先校验connection是否已经closed,如果是则抛出connection manager has been closed异常
2、 检查是否已经在当前session中提供了connection,如果是,则直接返回connection对象
,否则调用代理返回connection对象。再看代理类BorrowedConnectionProxy,这个类含有一个connectionManager的成员变量,查看其实现InvocationHandler 接口方法invoke的代码
从以上方法可以看出,当我们调用session.connection时,其实调用的就是ConnectionManager的一个实例(这个实例就是Session的一个成员变量)的方法:
public Connection getConnection() throws HibernateException {
if ( isClosed ) {
throw new HibernateException( "connection manager has been closed" );
}
if ( connection == null ) {
openConnection();
}
return connection;
}
也就是一下方法(留意注释)
/**
* Pysically opens a JDBC Connection.
*
* @throws HibernateException
*/
private void openConnection() throws HibernateException {
if ( connection != null ) {
return;
}
log.debug("opening JDBC connection");
try {
//从连接池中获取连接,并设置为成员变量(这个变量就是先前close的那个了)
connection = factory.getConnectionProvider().getConnection();
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"Cannot open connection"
);
}
callback.connectionOpened(); // register synch; stats.connect()
}