一、Spring为事务管理提供了三个接口:
1)PlatformTransactionManager:事务管理器,涵盖了事务提交,回滚等操作信息,spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现
2)TransactionDefinition:事务定义信息,涵盖了隔离级别、传播行为、超时、只读等操作信息。
3)Transactionstatus:事务具体运行状态,isNewTransaction() hasSavePoint()等等
二、TransactionDefinition的介绍
1)如果不考虑隔离性会引发的安全问题:脏读、不可重复读、幻读
脏读:一个事务读取到了另一个事务改了但还没提交数据,如果这些数据被回滚,则读到的是无效的数据。
不可重复读:一个事务读到另一个事务更新提交了的数据,导致在这个事务中,多次读取同一数据返回的结果有所不同。
2)事务的隔离级别:ISOLATION_...
隔离级别 | 含义 |
DEFAULT | 使用后端数据库默认的隔离级别 |
READ_UNCOMMITED | 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITED | 允许在并发事务提交后读取。可以防止脏读,但幻、不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,仍可发生幻读 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。在所有的隔离级别中是最慢的 |
3)事务的传播行为:事务的传播行为:PROPAGATION_...
1: PROPAGATION_REQUIRED
假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务。
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚
2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行。
比如说ServiceA.methodA没有事务,那么ServiceB.methodB也不会创建事务。
3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。
4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。
7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
三、事务管理的分类:
1)编程式事务管理:
*手动编写代码进行事务管理。(很少使用)
2)声明式事务管理:
*基于TransactionProxyFactoryBean的方式。(很少使用:因为需要为每个进行事务管理的类配置一个TransactionProxyFactoryBean进行增强)
*基于AspectJ的XML方式。(经常使用:一旦配置好之后,类上不需要添加任何东西)
*基于注解方式(经常使用:配置简单,但需要在业务层类上添加一个@Transactional的注解)
3)基于AspectJ的XML方式的配置
*首先在applicationContext.xml引入tx和aop的命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
*引入两个jar包:spring-aspectjs-3.2.0.RELEASE.jar、com。springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
*在applicationContext.xml配置
<!-- 事务管理配置 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务的通知(事务的增强) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pointcut1" expression="excction(* com.yangjf.service.*(..))" id=""/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
4)使用基于注解配置声明式事务
*在applicationContext.xml文件配置
<context:property-placeholder location="classpath:/hibernate.properties" />
<!-- 连接池配置 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${dataSource.driverClassName}" />
<property name="url" value="${dataSource.url}" />
<property name="username" value="${dataSource.username}" />
<property name="password" value="${dataSource.password}" />
</bean>
<!-- 事务管理配置 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />