Spring事务
spring针对不同的dao层框架,提供接口不同的实现类,常用的有两个
事务管理器 | 使用场景 |
---|---|
jdbc.datasource.datasourceTransactionManager | 用于Spring对于JDBC的抽象的支持,也可以适用于mybatis进行持久化的场景 |
orm.hibernate3.HibernateTransactionManager | 用于hibernate3 进行持久化 |
一、事务的五边形属性
1. 传播行为
七种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED — required | 默认的事务类型, 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS —-support | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_REQUIRED_NEW —-require_new | 表示当前方法必须运行在它自己的事务中,一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起, 意味着 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. |
PROPAGATION_NESTED —- nested | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意的是这种传播属性的嵌套事务意味着新启动的内层事务依赖于外部事务,外层事务的回滚可以引起内层事务的回滚。潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务提交后它才会被提交 |
PROPAGATION_NOT_SUPPORTED—- not support | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。 |
PROPAGATION_NEVER —– never | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_MANDATORY —mandatory | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.
2. 隔离规则
- 并发事务引起的问题
(1)脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的
(2)不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新
(3) 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别,一般是 REPEATABLE_READ 可重复读 |
ISOLATION_READ_UNCOMMITTED 未提交读 | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED 提交读 | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ 可重复读 | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE 串行化 | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
3. 只读 read-only
事务的第三个特性是它是否为只读事务。如果 事务只对后端的数据库进行读操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施
4. 事务超时
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束
5. 回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)
但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
二、 spring事务管理两种方式
第一种 编程式事务管理
直接通过编码使用Spring 的 TransactionTemplate来添加事务性边界,适用于完全的控制事务的边界,但是具有侵入性
// 执行事务模板的execute方法进行事务管理
public void saveUser(finnal User user){
transactionTemplate.execute(
new TransactionCallback(){
public Object doTransaction(TransactionStatus status){
try{
userDao.saveUser(user);
} catch (RuntimeException e){
status.setRollbackOnly(); // 发生异常,设置回滚事务
throw e;
}} );
}
第二种 声明式事务管理
(1) 基于xml配置文件实现
<!-- 第一步 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步 配置事务增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<!-- 设置进行事务操作的方法匹配规则 -->
<tx:method name="account*" propagation="REQUIRED" read-only = "true"/>
</tx:attributes>
</tx:advice>
<!-- 第三步 配置切面 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* cn.itcast.service.OrdersService.*(..))" id="pointcut1"/>
<!-- 切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>
(2) 基于注解实现
<!-- 第一步配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 第二步 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 第三步, 在实际的代码中添加注解,在类的上面添加注解意味着类中的所有方法全部都会纳入到事务的管理之中 -->
@Transactional
public class OrdersService {
......
.....
}
本博客参考了文章: http://www.mamicode.com/info-detail-1248286.html
说明: 本文大部分内容都是跟随者传播智课的教学视频学习而来,可以看做是翻译文章,只是自己吸收之后又书写一遍,加深自己的知识理解。