说一说@Transactional

添加@Transactional注解后,Spring会将每个方法执行封装为AOP的执行过程,每次执行到注解的方法时,会判断是否需要开启事务,如果事务创建成功,则执行业务逻辑,业务逻辑执行完成后,提交事务或异常后回滚事务。

在阿里巴巴java规范文档中,不建议使用@Transactional开启事务,而是通过手动的方式,是因为@Transactional 有很多失效的场景,或者是在复杂的场景下,如果不注意的情况下,容易形成长事务,占用了过多的数据库连接,导致数据库连接用完,或者是导致数据库锁表导致异常

扩展知识

手动添加事务方式

@Service
public class UserService {
    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    
    public void updateUser(){
        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            // 执行业务逻辑

            // 提交事务
            platformTransactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            platformTransactionManager.rollback(transaction);
        }
    }
    
}

有一种更简单的写法,内部自动开启事务,提交事务,或者回滚

@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void updateUser(){
        transactionTemplate.execute((TransactionCallback<Void>) status -> {
            // 执行业务逻辑

            return null;
        });
    }
    
}

@Transactional 原理

首选清楚mysql事务,首先开启事务,执行业务逻辑,然后提交或回滚事务。而Spring AOP是将这个过程封装了一层。
image.png

源码调用过程

在Spring中操作

@Service
public class UserService {

    @Transactional(rollbackFor = Exception.class)
    public void updateUser(){
       this.updateById();
        this.deptService.updateById();
    }
}

上面对updateUser方法添加了@Transactional类,在初始化Bean的时候,UserService会被代理,在代理调用过程如下

  1. CglibAopProxy.DynamicAdvisedInterceptor#intercept 方法拦截,此处为动态代理执行出

  2. ReflectiveMethodInvocation#proceed()方法,该方法是链式调用实现了MethodInterceptor的相关类的invoke方法

  3. 调用TransactionInterceptor#invoke方法,当前方法添加了@Transactional注解

image.png

在invokeWithinTransaction的关键代码
image.png
根据createTransactionIfNecessary内部信息,调用过程是
AbstractPlatformTransactionManager#getTransaction 获取事务信息
–> startTransaction() 开启事务
–> doBegin() 此处为最终Spring设置事务的方法
image.png
此时明白在调用代理对象下的updateUser()方法时,获取到mysql的连接,并将自动提交事务设置关闭

失效场景:

1、非public修饰的方法;Spring动态代理只能代理公共方法,私有或者静态方法都无法被代理
2、代码中使用try/catch处理的异常;在源码中只有抛出异常时才会被spring的代理执行的方法捕获,然后回滚事务
3、调用类内部的@Transactional方法;因为@Transactional是基于代理,调用内部方法时,直接通过this的方式调用,绕过了代理去执行方法
上述失效场景是由Spring AOP机制决定的

总结

在简单的场景下,直接使用@Transactional注解来进行编码,注意上述失效场景。但是在复杂的场景下,就容易产生长事务,因为我们知道,在调用方法的时候,就先获取到了mysql的连接,并且开启了事务,需要特别注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值