今天对 spring 的 aop 事务有了一个新的认识,所以赶紧把今天的学习记下来,希望在今后的学习中能够起到一些作用,也能对今天的认识做一次总结。
1.spring 分享
先看一段代码:
connection conn = conn.getconnection(); conn.setautocommit(false); …….. ……... conn.rollback(); conn.commit();
数据库的事务是针对 connection 的。
接着再看一段代码:( spring 中事务的一段学习代码,这段代码是把 spring 和 hibernate 结合在一起的,增加了理解上的难度,因为我的出发点一开始不要 hibernate ,就光用 jdbc 来进行数据库事务,但是没有其他好的代码,就这样吧)
public long addlineitem(long orderid, lineitem lineitem){ log("orderlistdaohibernate.addlineitem : start..."); orderlist orderlist = (orderlist) gethibernatetemplate().load(orderlist.class, orderid); lineitem.setorderlist(orderlist); gethibernatetemplate().saveorupdate(lineitem); gethibernatetemplate().saveorupdate(orderlist); log("orderlistdaohibernate.addlineitem : ending..."); return lineitem.getid(); }
在这个代码的配置文件中,把 addlineitem 做为一个切入点,进行事务,也就是说,在 addlineitem 的外面,再包上一层事务的外壳。
但是这个时候,问题出来了,事务是针对 connection 的,而上面的两个连续的 hibernatetemplate 执行的 saveorupdate 中的 connection 必须是一致才能用事务, spring 怎么做到这一点的呢?(这个问题也就是在找 spring 的事务例子前,我想的 spring 中用 jdbc 来进行事务,怎么样让 connection 保持一致呢?但是没有 jdbc 的例子,只有整合 hibernate 或者 ibatis 的例子,但是,我想,原理是一样的吧。)
解决问题的思路: hibernatetemplate 中的 connection 必定一致。那么就从 hibernatetemplate 入手。
看 spring 的源代码,既然是 hibernate ,那么,就没有 connection 给你看,只有 session ,由 session 来管理 connection ,那么用事务来控制的话,这个 session 必定在所有该事务中是一致的。于是在 hibernatetemplate 中找到:
protected session getsession() { if (isalwaysusenewsession()) {return sessionfactoryutils.getnewsession(getsessionfactory(), getentityinterceptor()); } else if (!isallowcreate()) {return sessionfactoryutils.getsession(getsessionfactory(), false); } else {return sessionfactoryutils.getsession( getsessionfactory(), getentityinterceptor(), getjdbcexceptiontranslator()); } }
看来在 sessionfactoryutils 里面,接着在 sessionfactoryutils.getsession 中找:
这个方法太长了,太复杂了,从简,发现了非常关键的一点:
sessionholder sessionholder = (sessionholder) transactionsynchronizationmanager.getresource(sessionfactory);
假如 sessionholder 不等于空,说明,在事务中有这样一个还没有 commit 的 session ,那么就返回这个 session ,假如等于空,新建一个 session ,并且在事务里加入这个 session 。这段代码的意思大概是这样,太繁杂了,只能猜,也肯定是如此。
再看 gethibernatetemplate() 方法来自继承 hibernatedaosupport ,看了电子书《 spring- reference 》的第九章“ dao 支持”, dao 的支持类可以有好多,如: jdbcdaosupport , hibernatedaosupport , jdodaosupport 等等。
既然前面一开始就是从 jdbc 的 spring 事务控制引起的,那么看到了同样的 hibernatedaosupport---jdbcdaosupport ,那么 jdbcdaosupport 也应该有 getjdbctemplate() 这个方法,并且返回 jdbctemplate 这个类。
果然如此。
于是剖析 jdbctemplate 是不是和 hibernatetemplate 一样。果然一样。
注意到:
connection con = datasourceutils.getconnection(getdatasource());
connection 是从 datasourceutils.getconnection() 来的,继续跟踪 datasourceutils.getconnection() 。
找到:
connectionholder conholder = (connectionholder) transactionsynchronizationmanager.getresource(datasource);
和 hibernate 中的一模一样,因为没有了 session 的封装,条理在 jdbc 中更加清晰了。
至此, spring 的事务控制 已经全部搞定。
2.spring 事务管理的配置
看了上面同事学习 spring 的笔记后自己也觉得有新的理解,从什么地方说起呢?就从 spring 的事务配置说起吧。那么我们看看 contextconfig.xml 吧。
<bean id="sessionfactory" class="org.springframework.orm.hibernate.localsessionfactorybean"> <property name="datasource"> <ref bean="datasource" /> </property> <property name="mappingresources"> <list> <value>mf/org/user/user.hbm.xml</value> </list> </property></bean><bean id="transactionmanager" class="org.springframework.orm.hibernate.hibernatetransactionmanager"> <property name="sessionfactory"> <ref local="sessionfactory" /> </property> </bean><bean id="txproxytemplate" abstract="true" class="org.springframework.transaction.interceptor.transactionproxyfactorybean"> <property name="transactionmanager"> <ref bean="transactionmanager" /> </property> <property name="transactionattributes"> <props><prop key="save*">propagation_required,-exception</prop><prop key="remove*">propagation_required,-exception </prop><prop key="update*">propagation_required,-exception </prop><prop key="incress*">propagation_required,-exception </prop><prop key="*">propagation_required,readonly</prop> </props> </property> </bean><bean id="usermanager" parent="txproxytemplate"> <property name="target" ref="usermanagertarget" /></bean><bean id="usermanagertarget"class=" mf.org.hb.user.service.impl.usermanagerimpl"> <property name="userdao" ref="userdao" /></bean><bean id="userdao" class="mf.org.hb.user.dao.hibernate.userdaohibernate"> <property name="sessionfactory" ref="sessionfactory" /></bean>
以上就是一个完整的 spring 配置,是不是很熟悉呢,这里是用的 appfuse 的框架,呵呵。有那么点味道吧。
首先我们看看
<bean id="transactionmanager" class="org.springframework.orm.hibernate.hibernatetransactionmanager"> <property name="sessionfactory"> <ref local="sessionfactory" /> </property></bean>
这一个 bean 让 spring 为我们注入了什么呢?事务,对!我们把 hibernate 的事务注入到了 spring 的 ioc 容器之中了。然后我们再看看:
<bean id="txproxytemplate" abstract="true" class="org.springframework.transaction.interceptor.transactionproxyfactorybean"> <property name="transactionmanager"> <ref bean="transactionmanager" /> </property> <property name="transactionattributes"> <props><prop key="save*">propagation_required,-exception</prop><prop key="remove*">propagation_required,-exception </prop><prop key="update*">propagation_required,-exception </prop><prop key="incress*">propagation_required,-exception </prop><prop key="*">propagation_required,readonly</prop> </props> </property></bean>
这个 bean 又是让 spring 为我们注入了了什么呢?事务代理,对了!我们把事务的代理交给一个 txproxytemplate 的去做了,这样的好处我待会再说,现在我们看看下面的一些配置信息。
<prop key="save*">propagation_required,-exception</prop><prop key="remove*">propagation_required,-exception </prop><prop key="update*">propagation_required,-exception </prop><prop key="incress*">propagation_required,-exception </prop><prop key="*">propagation_required,readonly</prop>
这里就是事务处理时如果遇到异常信息,或者其他的原因时我们要求 spring 把当前的事务回滚了,这样才能不至于在数据库中产生垃圾啊。我们规定所有的 save,remove,update,incress 这样的方法开头的在出现一些问题后把事务给回滚了,看看我们写的: propagation_required,-exception 。
有人就会说 propagation_required 就可以回滚事务啊,为什么加上 ,-exception 呢?其实我以前也时这样想的,但这是不完全正确的,当然我们在处理一个事务时只要有一个 propagation_required 就可以了,但是当我们的业务逻辑中要求我们在一个事务代理中开启两个事务,这两个事务表面上没有联系,但是实际中又有很多联系的,比如我们上传附件和提交文档,这样两个操作我们可以分开,因为他们不是往一个表里插入数据,我们又不希望这两个操作写在一个 service 里,这样我们要是有一个业务只要上传附件呢?那样我们是不是又要再写一个方法啊!所以在开启两个事务时如果有一个抛出异常了,我们就要把上一个提交的事务回滚了,这样做我们就要用的 -exception 了,这样就完全满足我们的要求了,我也试过如果我写的是 propagation_required,-sqlexception 时,这样我们只会在出现 sqlexception 时事务回顾,出现其他的异常事务就不回滚了,好在 spring 可以让我们写如异常的基类就可以做到捕获任何异常,这样我们就写 -exception 好了。特殊情况在特殊处理吧。通用情况下我们还是这样的。
我们再看看:
<bean id="usermanager" parent="txproxytemplate"> <property name="target" ref="usermanagertarget" /></bean><bean id="usermanagertarget"class="mf.org.hb.user.service.impl.usermanagerimpl"> <property name="userdao" ref="userdao" /></bean><bean id="userdao" class="mf.org.hb.user.dao.hibernate.userdaohibernate"> <property name="sessionfactory" ref="sessionfactory" /></bean> 当然我们也可以写成:<bean id="usermanager" parent="txproxytemplate"> <property name="target"> <bean class="mf.org.hb.user.service.impl.usermanagerimpl"> <property name="userdao"> <ref bean="userdao"/> </property> </bean> </property></bean><bean id="userdao" class="mf.org.hb.user.dao.hibernate.userdaohibernate"> <property name="sessionfactory" ref="sessionfactory" /></bean>
这下我们解除以前的疑惑, parent="txproxytemplate" 知道我们为什么在上面先写了 txproxytemplate 的 bean 了吧,这样我们就没有必要再写一编了。是不是很方便? spring 的这些技巧还不只这些呢。这样我们就可以轻松利用以上这三个注入的类去做我们的逻辑了。
spring 就是要我们注入实现类,然后使用接口操作,这样耦合性就不是那么强了,这也体现了 spring 的工厂模式。而 aop 的 manager 又象我们熟知的代理模式吧 !
3.注意要点
在写配置的时候注意各个 manager 和 dao 之间的关系,以及 <ref= ”” > 之间的关系,清晰里面的关系才能更好的配置
原文: http://dev.firnow.com/course/3_program/java/javajs/200853/112246.html