一文读懂Spring的事务机制

本文介绍了Spring框架中的事务管理,包括声明式事务和编程式事务的使用。@Transactional注解的不同参数如REQUIRED,REQUIRES_NEW等的含义被详细解释,并通过买酱油的例子说明事务的概念。同时,文章提到了Spring事务的一个陷阱,即代码顺序并不等同于执行顺序,提醒开发者注意事务的正确使用。
摘要由CSDN通过智能技术生成

Spring框架是日常开发中必定会用到的框架,而它的事务机制,也是面试的重中之重,今天我们来讨论一下Spring的事务机制。

什么是事务

厨房没有酱油了,妈妈叫你去商店买一瓶回来。买酱油的过程,你给分成三个步骤:

1.去妈妈那拿钱

2.去商店挑好酱油

3.把钱给收银员

这3个步骤,只要其中一个步骤完不成,买酱油失败。只有全部完成,买酱油成功。这种一组操作,要么全部成功,要么全部不成功,就叫做事务。

什么是Spring事务

Spring支持声明式事务和编程式事务两种方式。

1.声明式事务

在SpringBoot中可以在方法上添加@Transanal注解来实现

    // 开启一个事务,并指定回滚异常类型为Exception。
    @Transactional(rollbackFor = Exception.class) 
    public void addUser(User user) throws Exception {
        // 对数据库进行操作。
        userDao.addUser(user);
    }

@Transanal注解参数分为以下几种方式:

REQUIRED:如果没有事务就新建一个事务,否则就加入已有事务,是默认的事务机制

REQUIRES_NEW:不管有没有事务,都新建一个事务。例子事务A下面有个事务B,事务A的回滚不会影响事务B,导致事务B回滚

NESTED:如果外层没有事务就新建一个事务,如果外层有事务就嵌入其他事务,事务A下面有事务B。如果事务A回滚,事务B也会回滚,但是事务B回滚不会影响事务A。

SUPPORTS:如果当前存在一个事务,则加入到该事务中;如果不存在事务,则不使用事务。这种传播行为通常用于测试场景。

MANDATORY:必须在一个事务中执行此操作,如果当前没有事务,则抛出异常。

NEVER:以非事务方式执行操作,如果当前存在一个事务,则抛出异常。

NOT_SUPPORTED:以非事务方式执行操作,如果当前存在一个事务,则挂起该事务。

2.编程式事务

声明式事务非常简单,方法上加一个注解就能搞定,但是它的范围太大了。如果你只是想要对方法中的某一段代码做事务处理,不想上升到整个方法,那么可以采用编程式事务。

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void createProduct() {
        //普通操作A
        .....

        //通过TransactionTemplateh类来获取平台事务管理器。
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        // 通过execute方法来启动一个事务。
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                try {
                    // 事务操作B
                    ........     
                } catch (Exception e) {
                   // 事务操作B发生异常,进行回滚
                    status.setRollbackOnly();
                    throw e;
                }
                return null;
            }
        });

       //操作C
       ........
    }

Spring事务的陷阱

    @Transactional(rollbackFor = Exception.class)
    public boolean addFollowRecord() {
        
        //插入一条记录
        CustomerFollowUpRecord insertRecord = new CustomerFollowUpRecord();
        insertRecord.setFsUserId("fs_123456");
        customerMapperExt.insert(record);

        ThreadPoolUtil.execute(() -> {
            
            // 查找记录
            CustomerFollowUpRecord record=customerMapperExt.getByFsUserId(insertRecord);
            
            //打印日志
            if (Objects.nonNull(record)){
                log.info("异步操作打印XXXXXXX");
            }
        });
        
        //其他操作
        doOtherThing();
    }

上面是一个Spring事务方法,先插入一条数据,然后异步从数据库查询出来,打印日志。但是实际场景跑的时候,你会发现,日志很有可能不会打印。

这是为什么呢?

因为@Transactional注解的方法,是要把方法里面的所有代码执行完毕,才会提交到数据库。也就是说实际的执行顺序可能是这样:
1.CustomerFollowUpRecordrecord=customerMapperExt.getByFsUserId(insertRecord);

2.customerMapperExt.insert(record);

这也是之前有个同事写的代码,看不出问题,然后我帮忙看了下,发现的。所以要注意Spring事务的陷阱,代码的书写顺序,不代表执行顺序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值