关于事务管理的概念这里就很少介绍了,在个人博客“JDBC事务之理论篇”中也有介绍。html
关于Spring的事务管理,主要是经过事务管理器来进行的。这里看个Spring事务管理的接口图:(来自博客https://www.cnblogs.com/yixianyixian/p/8372832.html)spring
大概就是TransactionDefinition接口的实现类对对事务的一些配置设置进行定义;PlatformTransaction接口的实现类,是对事务进行管理的,像commit啊rollback等操做;而后TransactionStatus接口的实现类,是得到事务的一些状态的。数据库
而后,对于事务管理,Spring提供了声明式和编程式的方法。 所谓编程式,就是在业务逻辑代码中精确地定位事务管理,而声明式事务管理,是基于AOP的,也就是说能够将事务管理代码和业务逻辑代码解耦。编程
在SpringBoot中使用Spring的事务管理很是简单,至关于Spring声明式事务管理,并且是注释版的。数组
咱们只须要用两个注解:并发
@EnableTransactionManagement @Transactional
@EnableTransactionManagement是要加载SpringBoot启动类上的,至关因而配置文件中的:app
<!-- 声明式事务管理 配置事物的注解方式注入--> <tx:annotation-driven transaction-manager="transactionManager"/>
而这个@Transaction是放在你要进行事务管理的类或者是方法上的。框架
在@Transaction注解中有一些属性能够设置:ide
@Transactional注解中经常使用参数说明测试
参数名称 |
功能描述 |
readOnly |
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor |
该属性用于设置须要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName |
该属性用于设置须要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor |
该属性用于设置不须要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName |
该属性用于设置不须要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation |
该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation |
该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的状况,一般使用数据库的默认隔离级别便可,基本不须要进行设置 |
timeout |
该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
这里要讲的也就设置的比较多的两个:
首先是propagation。翻译过来是传播行为。什么意思呢?
咱们通常都是将事务设置在Service层 那么当咱们调用Service层的一个方法的时候它可以保证咱们的这个方法中执行的全部的对数据库的更新操做保持在一个事务中,在事务层里面调用的这些方法要么所有成功,要么所有失败。那么事务的传播特性也是从这里提及的。
若是你在你的Service层的这个方法中,除了调用了Dao层的方法以外,还调用了本类的其余的Service方法,那么在调用其余的Service方法的时候,这个事务是怎么规定的呢,我必须保证我在我方法里掉用的这个方法与我自己的方法处在同一个事务中,不然若是保证事物的一致性。事务的传播特性就是解决这个问题的,“事务是会传播的”在Spring中有针对传播特性的多种配置咱们大多数状况下只用其中的一种:PROPGATION_REQUIRED:这个配置项的意思是说当我调用service层的方法的时候开启一个事务(具体调用那一层的方法开始建立事务,要看你的aop的配置),那么在调用这个service层里面的其余的方法的时候,若是当前方法产生了事务就用当前方法产生的事务,不然就建立一个新的事务。这个工做使由Spring来帮助咱们完成的。
这里作一个各个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 若是当前在事务中,即以事务的形式运行,若是当前再也不一个事务中,那么就以非事务的形式运行 这就跟日常用的普通非事务的代码只有一点点区别了。不理这个,由于我也没有以为有什么区别 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。
通常用的较多就这个PROPAGATION_REQUIRED
而后还要讲的要给属性是: Isolation,就是确认隔离机制。
这个就和JDBC中的隔离机制基本一致,是为了防止drity reads、non-repeatable reads、phantom reads而有的机制,隔离级别常量这里也不细讲了,和个人博客中记录的“JDBC事务管理理论篇”中的基本同样。
好的下面上代码来看例子:
在咱们的service层中的一个类中新建一个测试方法:
先在接口中添加声明:
public interface UserService { Map<String, Object> selectList(Map<String, Object> map); Map<String, Object> insert(User user); Map<String, Object> update(User user); Map<String, Object> delete(String id); void transactionTest(User user1, User user2); }
再看实现类:(其余方法的实现省略了)
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public void transactionTest(User user1, User user2) { // TODO Auto-generated method stub userMapper.insert(user1); System.out.println(1/0); userMapper.insert(user2); } }
而后在咱们service的新建方法中,咱们两次调用userMapper的insert方法,也就是说进行了两次数据库的插入操做。而且在中间人为制造了一个异常。
先来看没进行事务管理的效果:
测试类:
@RunWith(SpringRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持! @SpringBootTest //提供spring环境 public class TransactionTest { @Autowired private UserService userService; @Test public void testTransaction() { User user1 = new User(); User user2 = new User(); user1.setId("111111"); user1.setUserAccount("tt"); user1.setUserName("user1"); user1.setUserPassword("12312312"); user2.setId("222222"); user2.setUserAccount("bb"); user2.setUserName("user2"); user2.setUserPassword("12312312"); userService.transactionTest(user1, user2); } }
结果截图:
运行前的数据:
运行后:
可见,执行了一个insert以后,由于异常,后面那个insert就没执行了。
好的如今咱们加入事务管理:
加入启动事务管理注解:
@SpringBootApplication(scanBasePackages={"com.stuPayment.*"}) @EnableTransactionManagement @MapperScan("com.stuPayment.dao") public class StuPaymentApplication { public static void main(String[] args) { SpringApplication.run(StuPaymentApplication.class, args); } }
加入Transactional注解:
能够加在类上,这样的话,就类中的全部方法都事务管理,这里咱们加在方法上:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor=Exception.class) @Override public void transactionTest(User user1, User user2) { // TODO Auto-generated method stub userMapper.insert(user1); System.out.println(1/0); userMapper.insert(user2); }
再运行看效果:
由于这个时候是REQUIRED的传播级别,也就是说两个方法都是一个事务的,只要有错误,就会回滚。
最后再说几点注意的地方:
一、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到须要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需咱们指定方式来让事务回滚要想全部异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .若是让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
二、@Transactional 注解应该只被应用到 public 可见度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 可是这个被注解的方法将不会展现已配置的事务设置。
三、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你固然能够在接口上使用 @Transactional 注解,可是这将只能当你设置了基于接口的代理时它才生效。由于注解是不能继承的,这就意味着若是你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,并且对象也将不会被事务代理所包装(将被确认为严重的)。所以,请接受Spring团队的建议而且在具体的类上使用 @Transactional 注解。
参考过的博客:
这两个是介绍Spring的事务管理的综述的
https://www.cnblogs.com/yixianyixian/p/8372832.html
https://blog.csdn.net/donggua3694857/article/details/69858827
这两个是介绍传播行为的概念的,第一个讲概念能够,第二个的例子挺清晰:
https://blog.csdn.net/wwh578867817/article/details/51736723
https://blog.csdn.net/hsgao_water/article/details/52860380
SpringBoot中的事务管理:
https://blog.csdn.net/wohaqiyi/article/details/72895983
@Transactional注解介绍:
https://www.cnblogs.com/caoyc/p/5632963.html