本文所涉及内容,网上有些讨论,但没找到合适的,因此我再写写,给初学者看,以oracle数据库为例。
下面session专指hibernate的session,connection专指jdbc的连接。
主要讨论2个问题:“hibernate的session与连接的关系”、“程序中应该频繁打开、关闭session?,还是打开后数据库操作完成再关闭”
一、hibernate的session与连接的关系;
session与connection,是多对一关系,每个session都有一个与之对应的connection,一个connection不同时刻可以供多个session使用。
有连接池的情况下(缺省就有一个自带的)session关闭后,connection不一定关闭,hibernate缺省使用自带的连接池(connection.pool_size值为最大连接数),因此,session.close()后,还可以查询到应用占用的连接:
正常情况下,每个open的session都需要close掉,如果是单线程使用的话,那么查询oracle数据库的连接,则一直是一个connection,如果多线程使用的话,则可能是一个或多个(多线程的open如果错开时间,则一个,多线程如果几乎同时,则超过一个,小于或等于连接池数据),如果session只open,不close,则每次增加一个connection。
通常每次页面请求是一个线程,模拟多线程,可以在jsp中用frameset,每个frame的src指向一个不同的session查询。也可以加断点,访问多个页面,后访问的先从断点执行,这样多线程交叉执行session操作,会发现在连接池连接较少时,会增加连接进到池中。
下面是一个jsp例子,每个frame里面都访问了数据库:
<frameset cols="200,200,*" >
<frame src="<%=GlobalNames.getWEB_APP()%>/sysman/roleAction.do?method=findAllRoles" name="rolelist" marginwidth="0" marginheight="0" >
<frame src="<%=GlobalNames.getWEB_APP()%>/sysman/moduleList.do" name="modulelist" marginwidth="0" marginheight="0">
<frame src="<%=GlobalNames.getWEB_APP()%>/sysman/grantTree.do" name="granttree" marginwidth="0" marginheight="0">
</frameset>
下面是在open或close时,打印的信息:
Thread:Thread[http-8080-Processor21,5,main],session created:net.sf.hibernate.impl.SessionImpl@a75737
Thread:Thread[http-8080-Processor23,5,main],session created:net.sf.hibernate.impl.SessionImpl@ab835a
Thread:Thread[http-8080-Processor21,5,main]SESSION closed:net.sf.hibernate.impl.SessionImpl@a75737
Thread:Thread[http-8080-Processor21,5,main],session created:net.sf.hibernate.impl.SessionImpl@3cbb4b
Thread:Thread[http-8080-Processor21,5,main],session created:net.sf.hibernate.impl.SessionImpl@3cbb4b
Thread:Thread[http-8080-Processor22,5,main],session created:net.sf.hibernate.impl.SessionImpl@1443800
Thread:Thread[http-8080-Processor21,5,main],session created:net.sf.hibernate.impl.SessionImpl@3cbb4b
Thread:Thread[http-8080-Processor21,5,main]SESSION closed:net.sf.hibernate.impl.SessionImpl@3cbb4b
Thread:Thread[http-8080-Processor23,5,main]SESSION closed:net.sf.hibernate.impl.SessionImpl@ab835a
Thread:Thread[http-8080-Processor22,5,main]SESSION closed:net.sf.hibernate.impl.SessionImpl@1443800
Thread:Thread[http-8080-Processor21,5,main],session created:net.sf.hibernate.impl.SessionImpl@6279d
Thread:Thread[http-8080-Processor21,5,main]SESSION closed:net.sf.hibernate.impl.SessionImpl@6279d
可见是交叉进行的,如果connection.pool_size设置为1,则执行完后,oracle的数据库连接查看为1个,如果connection.pool_size设置为10的话,oracle的数据库连接查看为2或3个。
二、程序中应该频繁打开、关闭session?,还是打开后数据库操作完成再关闭;
没有连接池的情况下,打开关闭连接很耗资源,因此不能经常打开关闭连接,有连接池的情况下,打开或关闭连接并不耗资源,上面2种方式各有利弊,
1、可以在程序必经之路,比如filter或者某个父类的方法中,统一关闭session,
比如方法中 :session.find1; session.find2;session.find3;doSomeThing();
find1、2、3中,若都不关闭session,则是使用同一个连接,直到完成后,到filter或其他设定的必经之路才统一关闭(通过线程关联ThreadLocal) ;
对事务操作,及时提交回滚,但是session也统一到必经之路去关闭;
2、每次都open、close。
我的建议是采用方式2,即每次都open、close。
因为每次都打开、关闭并不太耗资源,且容易将数据库操作控制在数据库层,第1种方式,将数据库的关闭移动到其他层去处理了,不太好,且必经之路可能会有漏网之鱼,比如别人在plugIn时,使用数据库操作,忘记关闭,就泄露了。另外当大并发时,尽量用完连接就放回去供别的线程使用,否则如上面提到的如果doSomeThing();很耗时,导致连接等待,就影响其他线程比较严重了,虽然已经获得连接的线程能以最快速度处理完自己的事情。