Spring事务的传播行为与隔离级别——Spring学习笔记

2 篇文章 0 订阅
1 篇文章 0 订阅

一、什么是事务

事务指的是逻辑上的一组操作,这组操作要么都成功,要么都不成功。

比如:我去菜市场买菜,博主现在在杭州都用支付宝转账的形式。我买个一斤排骨用支付宝扫码转账形式来付款。转账这个过程简单来看可以拆分成两个步骤:
1.从我账户扣钱;
2.老板账户钱数增加。
这两个步骤必须是一个整体,可以看成是一个事务。不能说执行步骤1成功了,从我账户扣钱了,转给老板的时候因为一些原因出错了,老板的钱没增加。我的钱也不还我(事务回滚)。那我俩就会组队去支付宝门口耍赖去了。所以这里,要么两个步骤都执行成功。如果有一个执行失败就要进行事务的回滚,使两个步骤都失败。

二、事务的特性(ACID)

1. 原子性(Atomicity)
 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
  
2.一致性(Consistency)
 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
 
 比如转账,假设用户A和B两者的钱加起来一共是100元,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该依然是100元,这就是事务的一致性。

3. 隔离性(Isolation)
 隔离性是一个事务的执行不受其他事物的干扰
  
 比如当多个用户并发访问数据库时,操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
 
5.持久性(Durability)
 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失已提交事务的操作

三、事务的隔离级别

说隔离级别之前先说几个概念:

脏读 : 一个事务读取到另一事务未提交的数据。如果这些数据被回滚了,则读到的是无效数据。

不可重复读 : 在同一事务中,多次读取同一数据返回的结果有所不同。这是由于在查询间隔,被另一个事务修改并提交了。

不可重复读和脏读的区别:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

幻读(虚读) : 一个事务读到另一个事务已提交的insert数据。

幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的type='1'改为type='2'的操作,这时事务T2又对这个表中插入了一行type='1'的数据并提交。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行type='1'没有修改,其实这行是从事务T2中添加的,这就是发生了幻读。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

接下来说事务的隔离级别:

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
1.读未提交数据(会出现脏读, 不可重复读) 基本不使用

@Transactional(isolation = Isolation.READ_COMMITTED)
2.读已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = Isolation.REPEATABLE_READ)
3.可重复读(会出现幻读)

@Transactional(isolation = Isolation.SERIALIZABLE)
4.串行化或叫序列化

以上四种隔离级别由高到低为:4>3>2>1,最高的是SERIALIZABLE级别,最低的是READ_UNCOMMITTED级别,当然级别越高,执行效率就越低。像SERIALIZABLE这样的级别,就是以锁表的方式使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。

MySQL数据库中默认的隔离级别为REPEATABLE_READ (可重复读)。
SQLSERVER数据库中默认的隔离级别为READ_COMMITTED(读已提交)。
Oracle数据库中默认的隔离级别为READ_COMMITTED(读已提交)。(这里需要指出的是,Oracle仅仅支持SERIALIZABLEREAD_COMMITTED

博主所在公司使用的是MySql,设置的隔离级别为READ-COMMITTED。如果你们用的也是MySql可以使用:select @@tx_isolation;来进行查询。

四、事务的传播行为

@Transactional(propagation=Propagation.REQUIRED) 
1.如果有事务,那么加入事务,没有的话新建一个事务(默认情况下)

@Transactional(propagation=Propagation.REQUIRES_NEW) 
2.不管是否存在事务,都创建一个新的事务。如果原来有事物存在,则将原来的事物挂起,
待新的事物执行完毕后,继续执行原来挂起的老事务

@Transactional(propagation=Propagation.NOT_SUPPORTED) 
3.以非事务方式运行。如果有事物存在,则挂起当前事物,待执行完毕后,继续执行原来挂起的事务

@Transactional(propagation=Propagation.SUPPORTS) 
4.如果有事物存在,那就加入当前事务。如果没有事物存在,那就不用事务

@Transactional(propagation=Propagation.MANDATORY) 
5.必须在一个已有的事务中执行,如果不存在事物,则抛出异常(IllegalTransactionStateException)

@Transactional(propagation=Propagation.NEVER) 
6.以非事物运行,如果存在事物,则抛出异常(IllegalTransactionStateException与Propagation.MANDATORY相反)

@Transactional(propagation=Propagation.NESTED) 
7.如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,
嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,
但外部事务回滚将导致嵌套事务回滚

五、工作中遇到的那些坑

1.@Transactional 只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.。

2.spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。

3.仅仅在方法或类上标记@Transactional是不足以开启事物行为的,需要配置<tx:annotation-driven/>切记!

4.用spring事务管理器,由spring来负责数据库的打开,提交,回滚。默认遇到运行期异常(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的异常时回滚。
而遇到需要捕获的异常(throw new Exception("注释");)不会回滚,即遇到受检查的异常(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或受检查异常)时,需我们指定方式来让事务回滚,要想所有异常都回滚,要加上
@Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚可以加上 @Transactional(notRollbackFor=RunTimeException.class)

5.在有事务的方法中切记遇到异常时要向外抛出,不能捕获后悄悄的消化了,这样事务捕获不到异常是无法执行回滚的,或者在catch代码块中手工执行回滚。

6.在同一类中有两个方法testA()事务属性为REQUIREDtestB()事务属性为REQUIRED_NEW。如果testA()方法中通过this.testB()的形式“自我调用”testB()方法。这时testB()方法上配置的事务属性REQUIRED_NEW不会生效,而是会沿用testA()方法的事务属性REQUIRED。这是因为spring事务实现本质是代理,事务的配置属性是在代理对象中,而通过this形式调用不会调到代理对象,而是直接调用目标对象(target),因此事务配置属性无效。

暂时记录这些,后续工作中遇到事务相关问题还会回来进行补充!
如果文中有错误,或者不准确的地方,还请各位大佬指出!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值