Spring为事务管理提供了一流的支持。同时支持编程式事务和声明式事务。无论是编程式事务,还是声明式事务,在开发Spring应用工程中,开发者都不需要同具体的事务管理实现进行交互。
事务管理抽象是Spring提供的最为重要的一种抽象。秉承Spring的设计原则,对于事务管理而言,Soring的食物抽象具有如下几方面的优势:
● 对于采用手工控制事务,即程序控制事务的编程方式而言,Spring提供的事务抽象易于使用。
● 无论底层的事务API是什么,Spring都能够提供一致的编程模型。
● Spring支持以声明方式管理事务,这其中主要依赖于Spring AOP模块提供的功能。因此,Spring AOP在Spring事务抽象服务中起了重要作用。
● 能够同Spring的DAO抽象进行集成。
● 在不同事务服务之间切换,只会涉及到Spring配置文件的修改,而不会涉及到代码的修改。
数据库事务
通常,数据库事务都具备ACID的特性。
● A,即原子性(Atomicity)。表示组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有的操作成功执行,整个事务才提交,事务中任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到初始状态。继而保证事物的原子性。
● C,即一致性(Consistency)。事务操作将导致一致性的结果,即使得其操作的资源处于一致性状态。无论是事务成功提交,还是回滚,都必须保证资源状态的一致性。当然,这种一致性在很大程度上需要由应用保证,因此C同A、I、D存在很大区别。
● I,即隔离性(Isolation)。是事务执行过程中,其操作的资源的状态信息不能够被其他事务操作到。因此,不同事务不能够并发对同一数据资源进行读取或写入操作。在对RDBMS操作过程中,为保证隔离性,经常需要对表、列进行锁定操作。
● D即持久性(Durability)。一旦事务成功提交,则事务所导致的结果应该是持久的,即使系统瘫痪都需要保证持久性。
对于声明式事务,Spring支持各种事务管理器,它提供TransactionProxyFactoryBean类,供实现声明式事务使用。
对于编程式事务,Spring也支持各种事务管理器,而且,比声明式事务更灵活。
在Spring提供的所有事务管理器中,PlatformTransactionManager(org.springframework.transaction.PlatformTransactionManager)是最基本的接口也是很重要的接口。
开发者应该很清楚,Spring框架肯定会为它提供若干个实现,供Spring IOC实现控制反转使用。对于Hiberante,需要借助于 HiberanteTransactionManager 将会把事务处理的具体工作委派给Hiberante中的net.sf.hiberante.Transation对象。其实,Spring提供的所用事务管理器中,都是对底层事务对象的封装。它自身并没有实现底层事务的管理。这也符合Spring的设计初衷——“不重复发明轮子”。对HibernateTransactionManager的commit 和 rollback操作将委派给Transaction对象。
<bean id=”transactionManager”
class=”org.springframework.orm.hibernate.HibernateTransactionManager”>
<property name=”sessionFactory”>
<ref local=”sessionFactory”/>
</property>
</bean>
HibernateTransactionManager就是实现PlatformTransactionManager接口,而且还是通过Spring IoC容器加载的。
Spring主要涉及到的事务管理器有:
● JDBC事务:借助于DataSourceTransactionManager实现。
● JTA事务:借助于JtaTransactionManager、WebLogicJtaTransactionManager实现。
● Hibernate事务:借助于HibernateTransactionManager实现。
● JDO事务:借助于JdoTransactionManager实现。
● OJB事务:借助于PersistenceBrokerTransactionManager实现。
● JMS事务:借助于JmsTransactionManager实现。
对于JDBC事务而言,需要配置DataSourceTransactionManager。配置示例如下。
<bean id=”transactionManager”
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=”dataSource”>
<ref local=”dataSource”/>
</property>
</bean>
<bran id=”dataSource”
class=”org.springframework.jndi.JndiObjectFactoryBean”>
<property name=”jndiName”>
<value>java:/MySqlDS</value>
</property>
</bean>
开发者需要为它准备dataSource 取值。至于dataSource的具体来源,DataSourceTransactionManager可以不用关注。这或许就是IoC的好处,而且还可以在产品部署场合动态切换。DataSourceTransactionManager会将事务 commit 和 rollback 操作委派给java.sql.Commection。
对于JDO事务而言,需要配置JdoTransactionManager。配置如下:
<bean id=”transactionManager”
class=”org.springframework.orm.jdo.JdoTransactionManager”>
<property name=”persistenceManagerFactory”>
<ref local=”persistenceManagerFactory”/>
</property>
</bean>
对于JTA事务而言,需要配置JtaTransactionManager或者WebLogicJtaTransactionManager实现。配置如下:
<bean id=”transactionManager”
class=”org.springframework.transaction.jta.JtaTransactionManager”>
<property name=”userTransactionName”>
<value>java:comp/UserTransaction</value>
</property>
</bean>
其中,指定了userTransactionName 属性。默认时,JtaTransactionManager设定userTransactionName属性取值为java:comp/UserTransaction,因此如果目标JTA实现的UserTransaction 名为java:comp/UserTransaction,则不用给出userTransactionName属性。当然,除了userTransactionName 属性外,还具有transactionManagerName、jndiTemplate、autodetectTransactionManager、等属性。其中,TransactionManagerName指定事务管理器名字;jndiTemplate 指定JNDI 模版,供查找事务管理器和 UserTransaction对象使用;autodetectTransactionManager用于指定是否自动查找事务管理器(默认时为true)。
声明式事务
借助Spring AOP,Spring提供了 TransactionProxyFactoryBean,它是为简化声明式事务处理而引入的代理工厂 JavaBean。当然,也可以使用ProxyFactoryBean 和 TransactionInterceptor的组合。在TransactionProxyFactoryBean 内部使用了事务拦截器,即 TransactionInterceptor。
使用声明式事务管理器,首先要搭建SSH框架,添加 Spring AOP JAR包,添加方法详见上篇:http://blog.sina.com.cn/s/blog_655974a60101c891.html#bsh-75-262512748
事务配置
首先在/WEB-INF/applicationContext.xml添加以下内容:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
注:这是作为公共使用的事务管理器Bean。这个会是事先配置好的,不需各个模块各自去配。
下面就开始配置各个模块所必须的部分,在各自的applicationContext-XXX-beans.xml配置的对于事务管理的详细信息。
首先就是配置事务的传播特性,如下:
<!-- 配置事务传播特性 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
<tx:method name="apply*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.testAda.test.model.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>
事务的几种传播特性
1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
需要注意的地方:
(1) advice(建议)的命名:由于每个模块都会有自己的Advice,所以在命名上需要作出规范,初步的构想就是模块名+Advice(只是一种命名规范)。
(2) tx:attribute标签所配置的是作为事务的方法的命名类型。
如<tx:method name="save*" propagation="REQUIRED"/>
其中*为通配符,即代表以save为开头的所有方法,即表示符合此命名规则的方法作为一个事务。
propagation="REQUIRED"代表支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
(3) aop:pointcut标签配置参与事务的类,由于是在Service中进行数据库业务操作,配的应该是包含那些作为事务的方法的Service类。
首先应该特别注意的是id的命名,同样由于每个模块都有自己事务切面,所以我觉得初步的命名规则因为 all+模块名+ServiceMethod。而且每个模块之间不同之处还在于以下一句:
expression="execution(* com.test.testAda.test.model.service.*.*(..))"
其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。
(4) aop:advisor标签就是把上面我们所配置的事务管理两部分属性整合起来作为整个事务管理。
图解:
下面给出详细的applicationContext.xml 文件的注释:
<?xml version="1.0" encoding="gb2312"?>
<!-- Spring配置文件的文件头,包含DTD等信息-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--定义数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 定义数据库驱动-->
<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
<!-- 定义数据库url-->
<property name="url"><value>jdbc:mysql://localhost:3306/spring</value></property>
<!-- 定义数据库用户名-->
<property name="username"><value>root</value></property>
<!-- 定义数据库密码-->
<property name="password"><value>32147</value></property>
</bean>
<!--定义一个hibernate的SessionFactory-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 定义SessionFactory必须注入DataSource-->
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<list>
<!--以下用来列出所有的PO映射文件-->
<value>Person.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<!--此处用来定义hibernate的SessionFactory的属性:
不同数据库连接,启动时选择create,update,create-drop-->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 定义事务管理器,使用适用于Hibernte的事务管理器-->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!-- HibernateTransactionManager bean需要依赖注入一个SessionFactory bean的引用-->
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<!--定义DAO Bean , 作为事务代理的目标-->
<bean id="personDaoTarget" class="lee.PersonDaoHibernate">
<!-- 为DAO bean注入SessionFactory引用-->
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<!-- 定义DAO bean的事务代理-->
<bean id="personDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 为事务代理bean注入事务管理器-->
<property name="transactionManager"><ref bean="transactionManager"/></property>
<!--