ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

事务控制:事务不会滚?

2022-09-08 20:03:48  阅读:200  来源: 互联网

标签:语句 控制 事务 回滚 try DDL catch 不会


可能原因一、try-catch中的代码报错,但是事务未回滚

1、问题描述

由于上传文件时通过异步来处理数据,故在异步处理数据之前向日志表中插入一条数据,处理状态为:处理中,等到处理结束,再修改处理状态为成功或失败。由于往数据库插入数据可能会抛异常,故用try/catch来处理,在正常情况下不会报错,故处理状态为成功;如果出现异常就catch,进入修改日志的处理状态为失败。上传文件并解析入库的过程可能成功也可能失败,故要使用try/catch。那么这种情况下,由于try/catch不会抛出异常,故事务不会回滚,那该如何处理呢?

代码如下:

   @Async
    @Transactional(rollbackFor = Exception.class)
    public void saveOutstoreInfo(Outstore outstore, UserVO userVo, List<String> codes, String path,String outstoreUploadLogTabName,String outstoreTableName,String outstoreDetailTableName,String outstoreCodeTableName) {
        try {
            // 记录日志,注意:接口调用时需要记录日志,定时任务不需要记录日志
            if (StringUtils.isNotBlank(path)) {
                uploadLog = insertUploadLog(outstore.getEntityId(), outstore.getOrderNo(), outstoreUploadLogTabName, userVo, outstore.getOrderTime(), path);
            }
            // 遍历追溯码,先获取关联关系码信息
            for (String code : codes) { // 切记不要在for循环内进行异步调用
                ...// 如果上面的都成功了,则修改日志状态未处理成功
                if (StringUtils.isNotBlank(path)) {
                    ....
                    outstoreUploadLogService.update(uploadLog);
                }
            }
        } catch (Exception e) {
            log.info(e.getMessage());
            // 手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            // 如果出现异常,则修改处理状态为失败
            if (StringUtils.isNotBlank(path)) {
                ...
                outstoreUploadLogService.update(uploadLog);
            }
        }

    }

2、解决办法

在catch中抛出所捕获的异常,或者添加下面代码手动回滚。

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

总结:

1.使用@Transactional(rollbackFor = Exception.class)注解,使用try-catch必须在catch语句中抛出异常,不然事务不回滚;

2.@Transactional注解,默认识别RuntimeException,只能在抛出RuntimeException或者Error时才会触发事务回滚,可在catch语句中直接抛出RuntimeException;

3.代码中有try-catch,需要事务回滚的话,最好添加手动回滚。

可能原因二、事务内有DDL语句

1、了解

mysql中ddl事务控制:

(1)、当执行到DDL语句时,会隐式的将当前回话的事务进行一次“COMMIT”操作,因此在MySQL中执行DDL语句时,应该严格地将DDL和DML完全分开,不能混合在一起执行。

(2)、为什么DDL语句会隐式提交?
因为DDL是数据定义语言,在我们的数据库中承担着创建,删除和修改的重要的职责。一旦发生问题,带来的后果很可能是不可估量的。二是在每执行完一次后就进行提交,可以保证流畅性,数据不会发生阻塞,同时也会提高数据库的整体性能。

(3)、DDL和DML混和执行可能带来的风险:事务中有一个DDL语句,导致事务“中途”提交一次,等最后发现异常要回滚却发现只能回滚一部分。

2、问题描述

共有四张表,前面三张表由于要分表,故先用存储过程创建表,再插入数据,第四个表b_code_year不需要建表,只插入数据。在往第四张表插入数据时认为制造一个空指针异常,结果发现前面两张表中事务没有回滚,而第三张表事务回滚了,第四张表由于报错没有数据。

3、解决办法

将创建表的语句放到事务外面先执行,即所有的表都创建完成再开启事务。

代码如下:

@Override
    public String scanCodeOutstore(JSONObject jsonObj) { // 该方法不进行事务控制
     ....
// 存储过程创建这三张表,注意创建表的语句不能放到事务中,因为DDL语句会自动提交,导致事务控制失败 buildOutstoreTable(tableName); //查询出库单据明细表是否存在,如果不存在则创建明细表,如果存在则新增或修改记录 buildDetailTable(tableName); //查询出库码表是否存在,如果不存在则创建出库码表 buildCodeDetailTable(tableName); // 异步调用,在该方法中进行事务控制 outStoreAsycService.saveOutstoreInfo(xxx); return null; }

saveOutstoreInfo方法:

@Async
    @Transactional(rollbackFor = Exception.class)
    public void saveOutstoreInfo(xxx) {
     // 切记不要在for循环内try/catch
     try { // DML 语句 } catch (Exception e) { log.info(e.getMessage()); // 手动回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();        } }

 

标签:语句,控制,事务,回滚,try,DDL,catch,不会
来源: https://www.cnblogs.com/zwh0910/p/16670683.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有