今天对 spring 的 AOP 事务有了一个新的认识所以赶紧把今天的学习记下来希望在今后的学习中能够起到一些作用也能对今天的认识做一次总结
spring 分享
先看一段代码
Connection conn = ConngetConnection();
connsetAutoCommit(false);
……
……
connrollback();
mit();
数据库的事务是针对 Connection 的
接着再看一段代码( spring 中事务的一段学习代码这段代码是把 spring 和 hibernate 结合在一起的增加了理解上的难度因为我的出发点一开始不要 hibernate 就光用 jdbc 来进行数据库事务但是没有其他好的代码就这样吧)
public Long addLineItem(Long orderId LineItem lineItem){
log(OrderListDAOHibernateaddLineItem : Start);
OrderList orderList = (OrderList) getHibernateTemplate()load(OrderListclass orderId);
lineItemsetOrderList(orderList);
getHibernateTemplate()saveOrUpdate(lineItem);
getHibernateTemplate()saveOrUpdate(orderList);
log(OrderListDAOHibernateaddLineItem : Ending);
return lineItemgetId();
}
在这个代码的配置文件中把 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 SessionFactoryUtilsgetNewSession(getSessionFactory() getEntityInterceptor());
}
else if (!isAllowCreate()) {
return SessionFactoryUtilsgetSession(getSessionFactory() false);
}
else {
return SessionFactoryUtilsgetSession(
getSessionFactory() getEntityInterceptor() getJdbcExceptionTranslator());
}
}
看来在 SessionFactoryUtils 里面接着在 SessionFactoryUtilsgetSession 中找
这个方法太长了太复杂了从简发现了非常关键的一点
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManagergetResource(sessionFactory);
假如 sessionHolder 不等于空说明在事务中有这样一个还没有 commit 的 session 那么就返回这个 session 假如等于空新建一个 session 并且在事务里加入这个 session 这段代码的意思大概是这样太繁杂了只能猜也肯定是如此
再看 getHibernateTemplate() 方法来自继承 HibernateDaoSupport 看了电子书《 springreference 》的第九章 Dao 支持 Dao 的支持类可以有好多如 JdbcDaoSupport HibernateDaoSupport JdoDaoSupport 等等
既然前面一开始就是从 jdbc 的 spring 事务控制引起的那么看到了同样的 HibernateDaoSupportJdbcDaoSupport 那么 JdbcDaoSupport 也应该有 getJdbcTemplate() 这个方法并且返回 JdbcTemplate 这个类
果然如此
于是剖析 JdbcTemplate 是不是和 HibernateTemplate 一样果然一样
注意到
Connection con = DataSourceUtilsgetConnection(getDataSource());
Connection 是从 DataSourceUtilsgetConnection() 来的继续跟蹤 DataSourceUtilsgetConnection()
找到
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManagergetResource(dataSource);
和 Hibernate 中的一模一样因为没有了 session 的封装条理在 jdbc 中更加清晰了
至此 spring 的事务控制 已经全部搞定
Spring 事务管理的配置
看了上面同事学习 spring 的笔记后自己也觉得有新的理解从什么地方说起呢?就从 spring 的事务配置说起吧那么我们看看 contextConfigxml 吧
<bean id=sessionFactory class=orgspringframeworkormhibernateLocalSessionFactoryBean>
<property name=dataSource>
<ref bean=dataSource />
</property>
<property name=mappingResources>
<list>
<value>mf/org/user/Userhbmxml</value>
</list>
</property>
</bean>
<bean id=transactionManager class=orgspringframeworkormhibernateHibernateTransactionManager>
<property name=sessionFactory>
<ref local=sessionFactory />
</property>
</bean>
<bean id=txProxyTemplate abstract=true class=orgspringframeworktransactioninterceptorTransactionProxyFactoryBean>
<property name=transactionManager>
<ref bean=transactionManager />
</property>
<property name=transactionAttributes>
<props>
<prop key=save*>PROPAGATION_REQUIREDException</prop>
<prop key=remove*>PROPAGATION_REQUIREDException </prop>
<prop key=update*>PROPAGATION_REQUIREDException </prop>
<prop key=incress*>PROPAGATION_REQUIREDException </prop>
<prop key=*>PROPAGATION_REQUIREDreadOnly</prop>
</props>
</property>
</bean>
<bean id=userManager parent=txProxyTemplate>
<property name=target ref=userManagerTarget />
</bean>
<bean id=userManagerTarget
class= hbuserserviceimplUserManagerImpl>
<property name=userDAO ref=userDAO />
</bean>
<bean id=userDAO class=hbuserdaohibernateUserDAOHibernate>
<property name=sessionFactory ref=sessionFactory />
</bean>
以上就是一个完整的 spring 配置是不是很熟悉呢这里是用的 Appfuse 的框架呵呵有那么点味道吧
首先我们看看
<bean id=transactionManager class=orgspringframeworkormhibernateHibernateTransactionManager>
<property name=sessionFactory>
<ref local=sessionFactory />
</property>
</bean>
这一个 bean 让 spring 为我们注入了什么呢?事务对!我们把 hibernate 的事务注入到了 spring 的 IOC 容器之中了然后我们再看看
<bean id=txProxyTemplate abstract=true class=orgspringframeworktransactioninterceptorTransactionProxyFactoryBean>
<property name=transactionManager>
<ref bean=transactionManager />
</property>
<property name=transactionAttributes>
<props>
<prop key=save*>PROPAGATION_REQUIREDException</prop>
<prop key=remove*>PROPAGATION_REQUIREDException </prop>
<prop key=update*>PROPAGATION_REQUIREDException </prop>
<prop key=incress*>PROPAGATION_REQUIREDException </prop>
<prop key=*>PROPAGATION_REQUIREDreadOnly</prop>
</props>
</property>
</bean>
这个 bean 又是让 spring 为我们注入了了什么呢?事务代理对了!我们把事务的代理交给一个 txProxyTemplate 的去做了这样的好处我待会再说现在我们看看下面的一些配置信息
<prop key=save*>PROPAGATION_REQUIREDException</prop>
<prop key=remove*>PROPAGATION_REQUIREDException </prop>
<prop key=update*>PROPAGATION_REQUIREDException </prop>
<prop key=incress*>PROPAGATION_REQUIREDException </prop>
<prop key=*>PROPAGATION_REQUIREDreadOnly</prop>
这里就是事务处理时如果遇到异常信息或者其他的原因时我们要求 spring 把当前的事务回滚了这样才能不至于在数据库中产生垃圾啊我们规定所有的 saveremoveupdateincress 这样的方法开头的在出现一些问题后把事务给回滚了看看我们写的 PROPAGATION_REQUIREDException
有人就会说 PROPAGATION_REQUIRED 就可以回滚事务啊为什么加上 Exception 呢?其实我以前也时这样想的但这是不完全正确的当然我们在处理一个事务时只要有一个 PROPAGATION_REQUIRED 就可以了但是当我们的业务逻辑中要求我们在一个事务代理中开启两个事务这两个事务表面上没有联系但是实际中又有很多联系的比如我们上传附件和提交文档这样两个操作我们可以分开因为他们不是往一个表里插入数据我们又不希望这两个操作写在一个 service 里这样我们要是有一个业务只要上传附件呢?那样我们是不是又要再写一个方法啊!所以在开启两个事务时如果有一个抛出异常了我们就要把上一个提交的事务回滚了这样做我们就要用的 Exception 了这样就完全满足我们的要求了我也试过如果我写的是 PROPAGATION_REQUIREDSQLException 时这样我们只会在出现 SQLException 时事务回顾出现其他的异常事务就不回滚了好在 spring 可以让我们写如异常的基类就可以做到捕获任何异常这样我们就写 Exception 好了特殊情况在特殊处理吧通用情况下我们还是这样的
我们再看看
<bean id=userManager parent=txProxyTemplate>
<property name=target ref=userManagerTarget />
</bean>
<bean id=userManagerTarget
class=hbuserserviceimplUserManagerImpl>
<property name=userDAO ref=userDAO />
</bean>
<bean id=userDAO class=hbuserdaohibernateUserDAOHibernate>
<property name=sessionFactory ref=sessionFactory />
</bean>
当然我们也可以写成
<bean id=userManager parent=txProxyTemplate>
<property name=target>
<bean class=hbuserserviceimplUserManagerImpl>
<property name=userDAO>
<ref bean=userDao/>
</property>
</bean>
</property>
</bean>
<bean id=userDAO class=hbuserdaohibernateUserDAOHibernate>
<property name=sessionFactory ref=sessionFactory />
</bean>
这下我们解除以前的疑惑 parent=txProxyTemplate 知道我们为什么在上面先写了 txProxyTemplate 的 bean 了吧这样我们就没有必要再写一编了是不是很方便? spring 的这些技巧还不只这些呢这样我们就可以轻松利用以上这三个注入的类去做我们的逻辑了
Spring 就是要我们注入实现类然后使用接口操作这样耦合性就不是那么强了这也体现了 Spring 的工厂模式而 AOP 的 manager 又象我们熟知的代理模式吧 !
注意要点
在写配置的时候注意各个 Manager 和 DAO 之间的关系以及 <ref= > 之间的关系清晰里面的关系才能更好的配置