spring boot加mybatis编程式事务在线程中回滚事务时,每隔一段时间就会提交事务

在Service层的线程方法,为了保证数据添加的正确所以加了锁。
线程里面有两个方法,为了保证添加时候数据正确,所以如果有方法出现错误就会实现事务回滚。

public String ticket(Integer sdid) {
        if (sd_id != null) {
            this.stock = schedulingService.findStock(sdid);
        }
        if (this.stock > 0) {
            TicketInspectRecord ticketInspectRecord = new TicketInspectRecord();
            ticketInspectRecord.setTir_tcr_id(1);
            ticketInspectRecord.setTir_tp_id(1);
            ticketInspectRecord.setTir_u_id(1);
            ticketInspectRecord.setTir_date(new Date());
            Thread thread = new Thread() {
                @Override
                public void run() {
                    synchronized (obj) {
                        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                        TransactionStatus status = transactionManager.getTransaction(def);
                        try {
                                                                ticketInspectRecordDao.inTicket(ticketInspectRecord);                                     
                                                                ticketInspectRecordDao.uStock(sd_id);//这一句必报错误
                            msg = "true";
                            transactionManager.commit(status);
                        } catch (Exception e) {
                            e.printStackTrace();
                            transactionManager.rollback(status);
                            msg = "false";
                        }
                    }
                }
            };
            thread.start();
            try {
                thread.join();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        return msg;
    }

奇怪的是在执行回滚的时候,有时候即便回滚了,方法ticketInspectRecordDao.inTicket(ticketInspectRecord)依旧会向数据库里插入数据,但是这种情况不是一直都会,项目重启的时候或者隔一段时间重新运行一次,就会出现这种情况。
研究了很久了,一直搞不明白问题在哪里

1个回答

问题可能出在 synchronized(obj) 上,这个 obj 是否是全局的一个公共对象呢?把它换成一个 xxx.class 类型试试呢。
如果这个 obj 对不同线程来说是不同实例,本质上这里就没办法对其中的代码块做到同步约束了,因为锁不同。
线程同步块应该受同一个锁保护。

doncha
doncha Parameter 'sd_id1' not found. Available parameters are [sd_id, param1],本来我是得传入sd_id的,但是我专门写成sd_id1错误,然后回滚之后就会出现我之前的问题,但是我如果换成别的异常,比如在第一个插入方法下面写一个int i = 10/0,这个异常就不会导致回滚之后还是插入数据
5 个月之前 回复
doncha
doncha 回复毕小宝: 我刚才再排查了一下,发现了一个问题,因为我是自己设置icketInspectRecordDao.uStock()方法,修改mybatis映射文件中的sql语句,让它出错来实现进入回滚方法。这个地方会报一个org.apache.ibatis.binding.BindingException错误,但是我如果换成另外一个运行时异常,那在执行回滚之后就并不会再插入数据了
5 个月之前 回复
wojiushiwo945you
毕小宝 回复doncha: 在事务处理过程中,我们可以通过TransactionStatus的setRollbackOnly方法标记事务回滚,所以commit(TransactionStatus)在具体提交事务之前会检查rollBackOnly状态,如果该状态没有被设置,则执行正常的事务提交操作。可以参考这篇看看:https://www.cnblogs.com/yaohuiqin/p/9486975.html
5 个月之前 回复
wojiushiwo945you
毕小宝 回复doncha:哦哦,这么厉害,找了根源了。那能不能找找相关配置,关掉清理方法时的自动提交。
5 个月之前 回复
doncha
doncha 但是还是那个问题,奇怪的是它并不是一直都有这个情况出现,是我在重启项目,或者隔个几分钟重新执行一次这方法时,事务才会自己提交,但是在提交了事务之后我又重新运行几遍代码,都没有问题
5 个月之前 回复
doncha
doncha 我自己排查了好久,发现在执行rollback方法的时候,它会DataSourceTransactionManager.class的doCleanupAfterCompletion方法,在里面有一步执行了 con.setAutoCommit(true)的时候,事务就自己提交上去了
5 个月之前 回复
wojiushiwo945you
毕小宝 回复doncha: 这么看来代码是没有问题的。
5 个月之前 回复
doncha
doncha 我的obj是在service类中定义的 private final static Object obj = new Object();
5 个月之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问