SpringBoot中使用事务的注意事项

一、事务的特征

事务一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元;是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID(Atomicity、Consistency、Isolation、Durability)特性。

1、原子性:一个事务是一个不可分割的工作单位,事务中包括的所有操作要么都做,要么都不做。

2、一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。

3、隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

4、持久性:持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。



二、Spring事务的配置方式

Spring支持编程式事务管理以及声明式事务管理两种方式。

1. 编程式事务管理

编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。

2. 声明式事务管理

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。



三、Spring的七种事务传播行为

类型说明
Propagation.REQUIREDSpring默认的传播机制,如果外层有事务,则将当前事务加入到外层事务中,一块提交,一块回滚。如果外层没有事务,则新建一个事务。
Propagation.REQUIRES_NEW每次都会新开启一个新的事务,同时把外层事务挂起,待当前事务执行完毕,再恢复上层事务的执行。如果外层没有事务,则执行当前新开启的事务
Propagation.SUPPORTS如果外层有事务,则加入到外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
Propagation.NOT_SUPPORTED以非事务方式执行操作,如果外层存在事务,则将外层事务挂起,执行完当前代码,再恢复外层事务。无论是否发生异常,都不会回滚当前的代码。
Propagation.NEVER以非事务方式执行操作,如果外层有事务就抛出异常
Propagation.MANDATORY与NEVER相反,使用外层事务,如果外层没有事务,则抛出异常
Propagation.NESTED如果外层存在事务,则嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

代码示例:点击查看另一博主的文章


四、事务的隔离级别

在一个典型的应用程序中,多个事务同时运行,经常会为了完成他们的工作而操作同一个数据。并发虽然是必需的,但是会导致以下问题:

脏读(Dirty read)

当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

例如:
周某的工资为8000,事务A中把他的工资改为10000,但事务A尚未提交。
与此同时,事务B正在读取周某的工资,读取到张三的工资为10000。
随后,事务A发生异常,而回滚了事务。周某的工资又回滚为8000。
最后,事务B读取到的周某工资为10000的数据即为脏数据,事务B做了一次脏读。

不可重复读(Nonrepeatable read)

在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的。

例如:
在事务A中,读取到周某的工资为8000,操作没有完成,事务还没提交。
与此同时,事务B把周某的工资改为10000,并提交了事务。
随后,在事务A中,再次读取周某的工资,此时工资变为10000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

幻读(Phantom reads)

一个事务在前后两次查询同一范围的时候,后一次查询看到了前一次查询没有看到的行。

例如:
目前工资为8000的员工有10人,事务A读取所有工资为8000的人数为10人。
此时,事务B插入一条工资也为5000的记录。
这是,事务A再次读取工资为8000的员工,记录为11人。此时产生了幻读。



五、Spring事务失效的原因

1、数据库引擎不支持事务

以 MySQL为例,MyISAM引擎是不支持事务操作的,InnoDB才支持事务,一般要支持事务都会使用InnoDB。

2、数据源没有配置事务管理器

数据源若没有配置事务管理器,事务不会生效。

3、不支持事务

比如配置事务的传播行为为:Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起。

4、没有被 Spring 管理

没有在类上标注@Service@Component等注解,这个类就不会被加载成一个Bean,这个类就不会被Spring管理了,事务就失效了。

5、事务所在方法的访问控制修饰符不是public

spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用。

6、同一类中相互调用

在同一类中调用该类的另一方法,另一方法的事务不会生效,因为同一类中方法相互调用不会被方法拦截器拦截到,因此没有经过Spring的代理类。默认只有在外部调用事务才会生效。

7、异常被try-catch捕获

如果在加有事务的方法内,使用了try…catch…语句块对异常进行了捕获,而catch语句块没有throw new RuntimeExecption异常,事务不会回滚。

8、异常类型错误或格式配置错误

Spring默认回滚的是:RuntimeException,如果想触发其他异常的回滚,需要在注解上配置,例如:@Transactional(rollbackFor = Exception.class)



总结

如果这篇博客对你有帮助的话,记得给我点个赞,你的鼓励是对我最大的支持!谢谢。◕‿◕。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值