Spring 事务是什么 ?事务的传播机制?

目录

1. 什么是事务?

2. Spring 事务三大基础设施

2.1 PlatformTransactionManager 平台事务管理器

2.2 TransactionDefinition 事务属性定义

2.3 TransactionStatus 事务状态

3. Transaction 注解

4. Spring 事务角色

5. @Transaction 注解属性

5.1 事务的回滚规则

5.2 事务的传播行为


1. 什么是事务?

了解过 MySQL 事务的同学应该都知道,事务通常是由多个 SQL 语句共同构成的一组逻辑操作单元,要么同成功,要么同失败。而我们的 Spring 事务也是一样的道理,它可以保证数据层或业务层的一系列操作要么同时成功,要么同时失败。其底层只是对 MySQL 的事务做了封装,因此如果你明白数据库的事务,了解 Spring 事务也是轻而易举。

2. Spring 事务三大基础设施

在 Spring 框架中,它给我们提供了三个和事务有关的最基础也最重要的三个接口或类。它们是 PlatformTransactionManager,TransactionDefinition,TransactionStatus。下面我来逐个说一下它们的作用。

2.1 PlatformTransactionManager 平台事务管理器

平台事务管理器 PlatformTransactionManager 是一个接口,里面定义了三个最基本的方法,它从上层规范了子类的行为,具体的代码实现交给了各个不同的平台。

这一点类似于我们之前所学习过的 JDBC ,sun 公司制定了一套数据库接口标准,各大数据库厂商去实现这些接口的方法,我们程序员不需要关心各个数据库厂商的具体实现。我们所熟知使用的 DatasourceTransactionManager 就是该接口的其中一个实现类。

它的源码我截取出来如下:

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
// 提交事务方法
    void commit(TransactionStatus var1) throws TransactionException;
// 回滚事务方法
    void rollback(TransactionStatus var1) throws TransactionException;
}
2.2 TransactionDefinition 事务属性定义

这个接口中定义了很多的属性,它更多是用来描述事务的具体规则,主要是 事务的隔离性,传播性,回滚规则,超时时间,事务是否只读。

我截取了该接口的源码如下

getIsolationLevel() 获取事务的隔离级别,如数据库的读未提交,读已提交,可重复读,序列化四种隔离级别;

getPropagationBehavior() 获取事务的传播行为,后面我会专门说到它,也是面试中会问到的点,这里先混个眼熟;

getTimeout() 获取事务的超时时间;

isReadOnly() 是否为只读;

2.3 TransactionStatus 事务状态
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
// 判断是否有保存点,类似于我们数据库中的事务保存点,回滚事务可以让其回滚至该点
    boolean hasSavepoint();
// 该方法可以把底层的方法刷新到数据库中去,现在用的不太多了
    void flush();
}

3. Transaction 注解

Spring 为我们提供了注解的方式开启事务,传统的编程式事务由于事务代码与业务代码糅合在一起,导致项目耦合度过高,因此在 Spring Boot 项目中,我们通常可以利用注解开启事务。

而且,@Transactions 注解不仅可以加在类上,也可以加在接口上,@Transaction 注解加载业务层实现类上,表示开启当前方法的事务,为了降低耦合度,我建议各位同学在业务层接口对各个接口也做 @Transactions 事务标记,标记当前接口中的所有方法是事务。

如果标注在接口上,表示该接口中的所有抽象方法都是事务,如下所示;

@Transactional
public interface UserService {
    
    User queryById(Long id);
    
    boolean transMoney();

}

如果标注在类上,表示该类中所有的方法都是事务,如下接口实现类;

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return userMapper.findById(id);
    }
    
    @Override
    public boolean transMoney() {
        userMapper.outMoney();
        userMapper.inMoney();
        return true;
    }
}

如果标注在方法上,表示当前方法是一个事务,如下代码;

@Override
    @Transactional
    public boolean transMoney() {
        userMapper.outMoney();
        userMapper.inMoney();
        return true;
    }

4. Spring 事务角色

Spring 事务中的角色分为事务管理员与事务协调员。

如下图

我们现在定义了一个两个接口转账接口。原本,我们的 outMoney 和 inMoney 分别是两个事务,互不相关。

然后定义一个转账业务方法,调用两个转账接口。我们在业务方法上加上@Transaction注解,表示开启 Spring 事务,这又是一个新的事务。

一旦我们开启了 Spring 事务,那么原本的 outMoney 和 inMoney 这两个事务就会加入到我的Spring 事务中,这样一来,三个事务就变成了一个事务,当我们的业务层中 tansfer 方法出现了错误,它就会回滚。就可以做到所有的操作同成功同失败的效果。

事务管理员:发起事务方,在 Spring 中通常指代业务层开启事务的方法。

事务协调员:加入事务方,在 Spring 中通常指代数据层方法,也可以是业务方法。

5. @Transaction 注解属性

我们在使用 @Transaction 这个注解的时候,可以为其配置很多属性

这里我们需要重点掌握的是 rollbackFor(回滚规则) 和 propagation(传播行为) 。

5.1 事务的回滚规则

我们通常所熟知的,在事务中,一旦发生异常,就要进行回滚。但是在 Spring 事务中,有一些异常它是不会回滚的,所以就需要我们手动设置。

在 Spring 事务中,只有遇到 Error 错误和 RuntimeException 运行时异常才会发生回滚,其余的异常即便发生事务也不会进行回滚。

例如在事务中如果发生了 IOException,它就不会回滚事务。如下图所示,

我们在两个转账操作之间加上 IOException,执行该方法,那么之际情况就是 outMoney 方法正常执行不会滚,inMoney 方法因为抛出异常不会运行。

想要解决此问题,我们就需要在 @Transaction 注解中配置 rollbackFor 属性,指定碰到哪些异常时回滚,如下图所示,我们直接将属性配置在接口上,降低耦合度

配置完成之后,此时该事务再遇到 IOException,就会回滚事务。


5.2 事务的传播行为

事务传播行为,我们也可以把它理解为 事务协调者对事务管理者的态度,什么意思呢?

刚才我说到,Spring 开启事务之后,所有的事务都会加入到Spring 开启的这个事务,使所有事务成为一个事务达到同成功同失败的目的。

我来举个例子,刚才的转账案例,我加入一个功能,不管转账是否成功,都需要在数据库中进行记录,说白了就是添加一个功能。

做法:正常情况下,我们需要建立数据库转账记录表——>Java中创建转账日志实体类——>编写数据访问层接口——>编写业务方法。然后将该业务方法也作为事务加入到 转账事务中就可以了。

但各位仔细看,不管转账是否成功,都需要在数据库进行记录,那么现在就出现问题了,如果我们按照原来的 Spring 事务操作是有问题的。如果转账事务成功,数据库日志会记录。如果转账事务不成功,事务回滚,数据库的日志表中还会有记录吗?日志是肯定也不会存在数据库中,如此一来,就无法达到我们的目的。

此时,就需要用到事务的传播行为这一属性,它需要在 @Transaction 这一注解中进行属性配置的。还是和刚才一样,我们在方法的接口上配置@Transaction(propagation = REQUIRES_NEW)。

所有传播行为如下表所示

可以看到,REQUIRES_NEW 中事务协调员在事务管理员有事务的情况下,它又单独开辟了一个事务,这样一来,我们的转账日志记录功能就与转账功能本身分割开了,成了两个事务,那么转账日志记录这个事务就不会随着转账事务的回滚而回滚,不管是否成功,都会进行记录,这样就得到了我们的目的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值