Spring与Hibernate结合的细节源码分析(一)
本文基于SPRING2.56,HIBERANTE3.25及Oracle10g classes14.jar驱动,介绍SPRING与HIBERNATE是如何配合的细节,如SESSION、 事务、数据库连接何时打开与关闭;如果调用不是发自请求,不经过FILTER(如定时器对SERVICE调用),如何做到从头到尾只用一个SESSION? 此时SESSION需不需要手动关闭?从SESSION取得的数据库连接需不需要关闭, 看完本文,你会清楚里面每一个细节。
两种访问系统的路径:
1、request-->filters(spring and struts)-->actions-->AOP(transaction)-->services-->dao-->db
2、timer(run)->AOP(transaction)-->services-->dao-->db
下面是调用链的图示
在WEB.XML配置了OpenSessionInViewFilter,整个请求只用一个SESSION,
在ACTION与SERVICE之间使用SPRING的AOP配置事务拦截器,所以事务范围涵盖整个SERVICE的方法调用,
DAO中一般使用SPRING的HibernateTemplate对象操作数据库
首先看OpenSessionInViewFilter在web.xml和事务拦截器相关配置
//下面开始到了ACTION的操作,action调用service,此service实际上是Spring动态生成的代理类,此代理类调用
//目标SERVICE类对应的方法前,要经过AOP拦截,下面我们对ACTION到SERVICE这中间经过的所有类方法代码分析一遍。
//我的应用在AOP层配置了一个事务拦截器,所以这中间的代码主要操作事务相关。
//动态生成的代理类代码看不到,从动态代理类下面的那个类(JdkDynamicAopProxy)开始吧递归调用拦截器//TransactionInterceptor--SPRING的事务拦截器实现类,这个方法可以根据事务管理器的种类分类if和else上下两部分,//如果配置的是org.springframework.orm.hibernate3.HibernateTransactionManager等非CallbackPreferringPlatformTransactionManager接口实现类//则看只需要看if部份//了解这里工作原理很重要://1、如果是请求发起的调用,流程路线为--FILTER打开Session,并且绑定到了线程信息中(TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session))),//在此方法里标志为旧SESSION,开始事务,调用目标,提交或回滚事务,判断如果是旧SESSION,则事务结束释放连接但不关闭SESSION,如果还用此SESSION再开启事务,再绑定另外的连接(所以上文说单SESSION并不等于同一个连接),最后在FILTER关闭SESSION。//2、如果不是请求发起的调用,如计划任务Timer.schedule(...)发起的SERVICE调用,流程为--在此方法打开SESSION,标志为新连接,开启事务,调用目标,提交或回滚事务,释放数据库连接,判断如果是新SESSION,则关闭SESSION。//总结如下://1、只要有配置了此事务拦截器,底层使用SPRING的HibernateTemplate.getSession()、getSession().connection()取得连接或Spring提供的各种查询,那么SESSION和数据库连接的关闭不需要你操心,但是stateMent和ResultSet要记得关闭。//2、如果要在Timer的run方法中多次调用SERVICE时,也要实现OpenSessionInViewFilter这样的效果,那么使用如这句,结束时要记得自已关闭SESSION。//打开SESSION后:TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));//最后关闭SESSION://SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());//SessionFactoryUtils.closeSession(sessionHolder.getSession());public Object invoke(final MethodInvocation invocation) throws Throwable {// 取得目标类名Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);// 通过类名及方法名,取得事务属性,即<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>这些内容final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);//取得类名+方法名全称final String joinpointIdentification = methodIdentification(invocation.getMethod());//没有事务属性 || 事务管理者不是CallbackPreferringPlatformTransactionManager类型(事务采用回调方法实现的方式)if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {//开始事务并创建事务信息对象(此对象主要包括事务属性和事务状态等信息),就是在这里调用了session.beginTransaction()方法//将事务信息以当前线程为KEY放入了TransactionAspectSupport.transactionInfoHolder对象中//注意:到这里如果当前取得到SessionHolder,那么SESSION就被标记成老SESSION,提交事务时就不会关闭之,反之那么session会在打开,并被标为新SESSION,提交事务时会把此SESSION关闭TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);Object retVal = null;try {// 调用下一下拦截器,如果没有下一个拦截器,那么目标类会被调用retVal = invocation.proceed();}catch (Throwable ex) {// 发生异常时,在这里回滚,回滚时,除了rollBack,还执行session.clear(),setFlushMode(NEVER)等动作// 新SESSION会在此被关闭,老的则不会completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清理当前线程对应的事务信息,cleanupTransactionInfo(txInfo);}//未发生异常,提交事务,如果是新SESSION,会在此被关闭,老的则不会commitTransactionAfterReturning(txInfo);return retVal;}else {// 回调式的事务管理器,SPRING中只提供了一个WebSphereUowTransactionManager的具体实现,/**CallbackPreferringPlatformTransactionManager接口的注释 * Extension of the {@link org.springframework.transaction.PlatformTransactionManager} * interface, exposing a method for executing a given callback within a transaction. * <p>Implementors of this interface automatically express a preference for * callbacks over programmatic <code>getTransaction</code>, <code>commit</code> * and <code>rollback</code> calls. Calling code may check whether a given * transaction manager implements this interface to choose to prepare a * callback instead of explicit transaction demarcation control.*/// 大意应该是可以由此接口的具体实现来决定事务的控制,我的E文一般般,有错的就砸砖try {Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,new TransactionCallback() {public Object doInTransaction(TransactionStatus status) {TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);try {//往下调用return invocation.proceed();}catch (Throwable ex) {if (txAttr.rollbackOn(ex)) {// A RuntimeException: will lead to a rollback.if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}else {throw new ThrowableHolderException(ex);}}else {// A normal return value: will lead to a commit.return new ThrowableHolder(ex);}}finally {cleanupTransactionInfo(txInfo);}}});// Check result: It might indicate a Throwable to rethrow.if (result instanceof ThrowableHolder) {throw ((ThrowableHolder) result).getThrowable();}else {return result;}}catch (ThrowableHolderException ex) {throw ex.getCause();}}}
//这篇文章在打开和提交事务、同一SESSION多次切换不同事务的细节没有提及,下一篇讲解。
//本人水平很有限,错漏之处难免,请各位指出,并请手下留情...呵呵 1 楼 sing4j 2010-01-26 学习,谢谢楼主。 2 楼 Sunny_kaka 2010-01-27 楼主辛苦了,能这样深入分析源码确实不容易. 3 楼 li445970924 2010-01-27 mark 等会来看
4 楼 fucktianya 2010-01-27 这么麻烦么?我记得不用这么多吧?
能讲讲使用openSessionInFilter的好处么?
我以前写的配置,都没有使用这个东西,所以不是很了解 5 楼 sunshy511 2010-01-27 正在学习中,谢谢分享! 6 楼 shan7719515 2010-01-27 标记一下,先谢了 7 楼 wuwenyu 2010-01-27 喜欢连载。。。。。 8 楼 a_nuo 2010-01-27 不是特别明白,回来在看 9 楼 heqishan 2010-01-27 学习了。期待下一篇 10 楼 lgdlgd 2010-01-27 fu cktianya 写道这么麻烦么?我记得不用这么多吧?
能讲讲使用openSessionInFilter的好处么?
我以前写的配置,都没有使用这个东西,所以不是很了解
配置的方法不是只有一种,我这只是其中一种,运行流程是一样的。
openSessionInFilter的好处:
1、在整个请求中只打开了一个SESSION,对象的创建少了。
2、避免页面出现延迟加载异常。 11 楼 liangsongzhe 2010-01-27 非常感谢^^ 12 楼 jefyjiang 2010-01-27 mark一下,以后学习!写得好! 13 楼 mooninday 2010-01-28 必须要mark下,工作做好了来学习 14 楼 Purking 2010-01-30 非常棒, 太感谢楼主了, 解决了一直以来的困惑. 15 楼 wangdgsc 2010-01-30 这个确实不错,分析的比较的详细,谢谢 16 楼 一剑飘红007 2010-03-18 学习了,谢谢楼主。 17 楼 lixia0417 2010-05-01 楼主的文章写得很详细,受教了,非常期待下一篇 18 楼 lixia0417 2010-05-02 楼主:这句话“
如果:事务是否已开始 && sessionHolder中没有SESSION或只有一个默认SESSION”有点模糊啊,我觉得是用AOP声明式事务代理时,在开启事务的时候,已经有了一个新建了一个SessionHolder了,所以 if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) 这个条件肯定是满足的。 19 楼 lgdlgd 2010-05-03 lixia0417 写道楼主:这句话“
如果:事务是否已开始 && sessionHolder中没有SESSION或只有一个默认SESSION”有点模糊啊,我觉得是用AOP声明式事务代理时,在开启事务的时候,已经有了一个新建了一个SessionHolder了,所以 if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) 这个条件肯定是满足的。
在使用JTA的环境中,sessionHolder中的Session不使默认KEY存放,doesNotHoldNonDefaultSession()方法返回值就是false了,条件就不满足了。