事务属性及事务分组
Posted jiyukai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事务属性及事务分组相关的知识,希望对你有一定的参考价值。
一、Spring事务属性
接上一节<<Spring的事务控制>>,Spring提供了@Transactional的注解来帮助控制事务,对于这个注解中涉及的几个属性需要说明和掌握一下
@Transactional(isolation=Isolation.DEFAULT,rollbackFor=ArithmeticException.class,timeout = -1,readOnly=false,propagation=Propagation.REQUIRED)
propagation传播行为:定义关于客户端和被调用方法的事物边界
isolation隔离级别:并发访问下数据库的全安性
timeout 事务超时:事务的最长持续时间,如果事务一直没有提交或回滚,那么超出该时间后,系统将自动回滚事务,单位秒。-1表示不超时,最终由底层数据库系统决定。
readOnly只读状态:只读事务不修改任何数据,可以优化查询操作。
rollbackFor和noRollbackFor:指定异常回滚或不回滚
其中关于隔离级别的配置主要有以下几种:
1. DEFAULT (默认)
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
2.READ_UNCOMMITTED (读未提交)
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3.READ_COMMITTED (读已提交)
保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
4.REPEATABLE_READ (可重复读)
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读
5.SERIALIZABLE(串行化)
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
关于事务传播属性的配置主要有以下几种:
传播行为 |
意义 |
REQUIRED |
业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务 |
NOT_SUPPORTED |
声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行 |
REQUIRESNEW |
属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行 |
MANDATORY |
该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。 |
SUPPORTS |
这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行 |
Never |
指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行 |
NESTED |
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效 |
二、事务传播属性案例
接着上一节的代码,假设在业务实现类PersonService中引入多个DAO,并且都涉及到了增删改操作,在都添加事务控制的前提下,如何做到各个DAO之间的事务互不影响。
如下代码:在业务方法operateMethod中引入logDao和personDao,如果logDao和personDao的操作都在一个事务中,那么operateMethod一旦出现异常,则两个Dao中的逻辑都会被回滚掉,如果两个Dao各自的事务传播属性都是REQUIRED,由于REQUIRED的定义是:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。则一旦operateMethod出现异常,两个Dao依然会被同时回滚。
如果需要做到operateMethod发生异常时,logDao不回滚,而personDao回滚,则需要将logDao的事务传播属性设置为REQUIRES_NEW,如此以来,执行到operateMethod方法时,会开启一个事务A,再走到logDao时,原先的事务A会挂起,开启一个新的事务B,待到logDao执行完毕,新的事务B提交,原先A的事务恢复,继续往下执行。
package com.jyk.spring.annotation.transaction.property; import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /* * 在一个service中调用多个dao */ @Service public class PersonService { @Resource private PersonDao personDao; @Resource private LogDao logDao; /* * @Transactional的使用位置 * 1、定义到方法上:当前方法应用的spring的声明式事务 * 2、定义到类上:当前类的所有方法都应用spring的声明式事务 * 3、定义到父类上:当执行父类的方法的时候应用Spring的声明式事务 */ @Transactional( readOnly=false, //读写事务,只读事务不修改任何数据 timeout = -1,//事务的超时时间没有限制 // noRollbackFor = ArithmeticException.class, //遇到数学异常不回滚 isolation = Isolation.DEFAULT, //事务的隔离级别 propagation = Propagation.REQUIRED //传播行为 ) public void operateMethod(Person person) { logDao.save();//打印日志<开启自己的事务> int myexception = 1/0; personDao.addPerson(person);//添加用户 } }
package com.jyk.spring.annotation.transaction.property; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Repository public class LogDao { @Resource private JdbcTemplate jdbcTemplate; //在事务中自己开启自己的事务 @Transactional( propagation = Propagation.REQUIRES_NEW ) public void save() { String sql = "insert into mylog(content) values(‘正在打印日志...‘)"; jdbcTemplate.update(sql); } }
三、事务分组
事务分组这一概念被广泛地应用于各大企业应用中,用来控制长链条的业务逻辑中,将业务切分为各小模块,并且各小模块处于互补影响的事务中,从而实现整个链条发生异常时,部分业务回滚,而部分业务不回滚的场景。
假设A->B->C->D这样一个调用场景,如果想实现整个链路异常时,只有C回滚,AB和D不回滚,A或B出现异常时AB回滚,D出现异常时D才会回滚,类似这种复杂的事务控制场景,需要如何实现?
很简单,如果看了《Spring的事务控制》和本节的内容,只要熟练的使用REQUIRE和REQUIRES_NEW即可控制,我们可将整个链路ABCD放在一个execute方法中,execute开启REQUIRE事务1,到A时使用REQUIRES_NEW属性开启新的事务2,同时B也加入到该事务2中,C加入到事务1中,同时D使用REQUIRES_NEW属性开启新的事务3,就能实现了。
思路如下:
REQUIRE
execute(){
AB();
C();
REQUIRES_NEW
D();
}
REQUIRES_NEW
AB(){
A();
B();
}
以上是关于事务属性及事务分组的主要内容,如果未能解决你的问题,请参考以下文章