为什么有的公司会禁用spring声明式事务

时间:2022-12-31 08:52:00

在之前我一直偏向于使用声明式事务,我一直觉得声明式事务比较好用。相比于编程式事务,使用声明式事务时只需要加上一个注解,spring就能够帮助我们完成所有的事务控制。反观编程式事务却需要我们自己去控制事务的提交和回滚,这种代码入侵的事务编程看起来也不是那么优雅。

但是最近在和朋友聊天时发现他们公司缺禁止使用声明式事务?这是为什么呢?

其实原因也很简单,虽然声明式事务方便好用,但是有一个致命的缺陷,就是一旦使用不当,很容易造成事务失效。下面这个博客里面我罗列了部分失效的情况,如果不了解的可以看一下。

spring 事务传播行为以及失效原因_极速小乌龟的博客-CSDN博客今天在查看以前写的代码时,看到了事务的使用,感觉自己对这一块并不是特别清晰,所以就系统的学习了一下。在学习过程中发现很多地方自己以前理解的还是有点不对,所以记录一下学习笔记,希望帮助到大家。备注:因为除了PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED,其他的都不是特别难以理解,所以我这里就只对这两个做了一下代码实例。当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。声明式事务为什么有的公司会禁用spring声明式事务https://blog.csdn.net/qq_35771266/article/details/128222174说实话这个对我触动也有点大,之前一直以为编程式事务是一个过时的东西,操作不方便也不好用。但是现在我知道了编程式事务最起码能避免事务的失效。不过就个人而言,我觉得声明式事务还是非常好用的,我们在开发时如果说比较简单的事务,或者能够肯定生命式事务不会出问题的情况下,我觉得使用声明式事务就可以了。要是非常重要的逻辑而且逻辑比较复杂不能够清晰判定事务不会出问题时,我觉得保险起见可以使用编程式事务。

一、编程式事务的使用

编程式事务的使用也很简单,就是代码比较多,写着有点小不爽。下面我来贴一下代码,各位看一下:

 @Autowired
    private PlatformTransactionManager transactionManager;


    /**
     * description: insertUser  插入用户信息<br>
     * @version: 1.0
     * @date: 2022/12/28 0028 下午 2:41
     * @author: William
     * @param user   用户信息
     * @return com.example.springbootdemo.common.response.Result
     */
    @ApiOperation(value = "插入用户信息")
    @PostMapping
    public Result insertUser(@RequestBody User user){
        //参数校验。。。
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
        boolean flag = false;
        try {
            // 数据库操作
            flag = userService.save(user);
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new MyException(ErrorCodeEnum.OPERATION_FAILED);
        }
        return flag?Result.ok():Result.error(ErrorCodeEnum.OPERATION_FAILED);
    }

 其实这么多代码就做了一步数据插入的操作,我们如果使用声明式事务,代码是什么样的呢?

/**
     * description: insertUser  插入用户信息<br>
     * @version: 1.0
     * @date: 2022/12/28 0028 下午 2:41
     * @author: William
     * @param user   用户信息
     * @return com.example.springbootdemo.common.response.Result
     */
    @ApiOperation(value = "插入用户信息")
    @PostMapping
    @Transactional(rollbackFor = Exception.class)
    public Result insertUser(@RequestBody User user){
        //参数校验。。。。
        boolean flag = userService.save(user);
        return flag?Result.ok():Result.error(ErrorCodeEnum.OPERATION_FAILED);
    }

真是没有对比就没有伤害。如果说在开发中每次用到这样写一遍,讲真内心是崩溃的。所以我们还需要对他进行一次升级。

二、升级版编程式事务

其实稍微留心我们就能发现,事务的开启,提交和回滚是固定的,唯一发生变化的也就是中间的业务逻辑。所以我们可以把公共的部分给它抽取出来就好了。具体实现代码如下:

package com.example.springbootdemo.common.utils;

import com.example.springbootdemo.common.enums.ErrorCodeEnum;
import com.example.springbootdemo.common.exception.MyException;
import com.example.springbootdemo.common.response.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.function.Function;

/**
 * @description: TransactionUtils <br>
 * @date: 2022/12/28 0028 下午 3:16 <br>
 * @author: William <br>
 * @version: 1.0 <br>
 */
@Component
public class TransactionUtils {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public <R,T> R queryItem(T t,Function<T,R> dbCallBack){
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
        R r;
        try {
            // 数据库操作
            r = dbCallBack.apply(t);
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new MyException(ErrorCodeEnum.OPERATION_FAILED);
        }
        return r;
    }
}

这样的话我们在来看看刚刚的代码:

    @Autowired
    private TransactionUtils transactionUtils;

    /**
     * description: insertUser  插入用户信息<br>
     * @version: 1.0
     * @date: 2022/12/28 0028 下午 2:41
     * @author: William
     * @param user   用户信息
     * @return com.example.springbootdemo.common.response.Result
     */
    @ApiOperation(value = "插入用户信息")
    @PostMapping
    public Result insertUser(@RequestBody User user){
        //参数校验。。。。
        boolean flag = transactionUtils.queryItem(user, u -> userService.save(user));
        return flag?Result.ok():Result.error(ErrorCodeEnum.OPERATION_FAILED);
    }

怎么样是不是舒服多了?看着也优雅了很多,这样我们在开发中相比于声明式事务也就多导入一个类,然后调用一次方法就好了。

不过上面这个版本也只是一个思路版本,如果小伙伴有需要使用的,我觉得可以按照这种思路自己去个性化定制就好了。

如果文章对你有所帮助的话,麻烦点赞关注呦,谢谢啦~