Spring自学日志03-事务

一、事务的基本概念

1.1、事务的ACID属性

提到事务,不可避免需要涉及到事务的ACID属性:

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

我们将严格遵循ACID属性的事务称为刚性事务。与之相对,期望最终一致性,在事务执行的中间状态允许暂时不遵循ACID属性的事务称为柔性事务,可参考传统事务与柔性事务,柔性事务的使用涉及到分布式事务方案,可以后续扩展,这里我们先将注意集中在事务实现原理上。

1.2、事务的隔离级别

根据SQL92标准,MySQL的InnoDB引擎提供四种隔离级别(即ACID中的I):

  1. 读未提交(READ UNCOMMITTED)
  2. 读已提交(READ COMMITTED)
  3. 可重复读(REPEATABLE READ)
  4. 串行化(SERIALIZABLE)

InnoDB默认的隔离级别是REPEATABLE READ,其可避免脏读不可重复读,但不能避免幻读,需要指出的是,InnoDB引擎的多版本并发控制机制(MVCC)并没有完全避免幻读,关于该问题以及隔离级别说明,可参考MySQL的InnoDB的幻读问题

1.3、事务的传播机制

Spring针对方法嵌套调用时事务的创建行为定义了七种事务传播机制,分别是

  1. PROPAGATION_REQUIRED,如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置,也是Spring事务默认的传播机制
  2. PROPAGATION_REQUIRES_NEW,创建一个新事务,如果存在当前事务,则挂起该事务
  3. PROPAGATION_SUPPORT,当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  4. PROPAGATION_NOT_SUPPORTED,始终以非事务方式执行,如果当前存在事务,则挂起当前事务
  5. PROPAGATION_MANDATORY,当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常
  6. PROPAGATION_NEVER,不使用事务,如果当前事务存在,则抛出异常
  7. PROPAGATION_NESTED,如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)

基本上从字面意思就能知道每种传播机制各自的行为表现,Spring默认的事务传播机制是PROPAGATION_REQUIRED,即如果当前存在事务,则使用当前事务,否则创建新的事务。

1.4、通过注解开启事务

在 Spring 项目中,我们可以直接使用注解 @Transactional 来标识一个类或方法支持事务。

//可以在类上
@Service
@Transactional
public class Target implements IUserService {
   ............
}


//也可以在类的方法上
@Service
public class Target implements IUserService {
    @Override
    @Transactional
    public void Transaction(UserEntity entity1, UserEntity entity2) {
		.........
    }
}

该注解建议在类上加在方法上加会有隐患,一个没有加@Transactional(MothodA)注解的方法,去调用一个加了@Transactional的方法(MothodB),不会产生事务

public class Target {
    //没有加@Transactional(MothodA)
    public void MothodA(UserEntity entity1, UserEntity entity2) {
        MothodB(entity1, entity2);
    }
    
    //加了@Transactional的方法(MothodB)
    @Transactional
    public void MothodB(UserEntity entity1, UserEntity entity2) {
		.........
    }
}

要知道原因首先了解@Transactional的原理。调用@Transactional方法,如何产生事务?

其实Spring的@Transactional,跟Spring AOP一样,都是利用了动态代理。

我们写了一个类,里面写了一个加了@Transactional注解的方法,这原本平淡无奇,什么用也没有,就像这样:

img

关键在于,Spring在检查到@Transactional注解之后,给这个对象生成了一个代理对象proxy:

img

代理对象的methodB,会先开启事务(beginTransaction),然后再去执行原先对象target的methodB,如果抛异常,则回滚(rollBack),如果一切顺利,则提交(commit)。

而最后注入Spring容器的,也正是这个带有事务逻辑的代理对象。所以我们调用methodB时会产生事务。

现在,我们写了一个新方法,methodA,里头去调用methodB:

img

从上面的分析,可以知道,我们最终拿到的,是代理对象。

那么代理对象的methodA是长什么样子的呢?长这样:

img

由于methodA没有加@Transactional注解,所以代理对象里面,直接就是target.methodA(),直接调用了原来对象的methodA。

这下就很清晰了,代理对象的methodA,去调用原来对象的methodA,原来对象的methodA,再去调用原来对象的methodB,而原来对象的methodB,是不具有事务的。事务只存在于代理对象的methodB. 所以整个方法也就没有事务了。

<iframe id="embed_dom" name="embed_dom" frameborder="0" style="display:block;margin-left:-244.5px; margin-top:-137.5px;width:489px; height:275px;" src="https://www.processon.com/embed/636db5ca1e085317c6a53f00"></iframe>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值