jfinal嵌套事务相关疑问

最近做一个异步导入,就是上传文件后即“受理”完成,向前端返回受理结果,然后后台新开一个线程,进行导入任务,导入完成后更新数据库任务表状态,如果中途验证或发生异常,更新任务状态并记录失败原因。核心代码如下:

//创建异步任务,向数据库任务表写入一条数据
new Thread(new Runnable() {//创建线程
    @Override
    public void run() {
        Db.tx(new IAtom() {//外部事物
            @Override
            public boolean run() throws SQLException {
                try {
                    //读取文件内容
                    //导入文件内容
                    //导入成功,将任务状态更新为成功,进度100%
                } catch (Exception e) {
                    //导入失败
                    Db.tx(new IAtom() {//内部事务
                        @Override
                        public boolean run() throws SQLException {
                            //在excel文件最后一列追加错误原因
                            //将任务状态更新为失败,保存错误原因
                            return true;
                        }
                    });

                    //返回false,本来希望更新任务状态后,外部事务回滚,但是最后发现内部事务也被回滚了
                    return false;
                }

                return true;
            }
        });
    }
}).start();

求指导,因为jfinal没有类似于spring的事务传播机制,所以暂时没有太好的办法。

评论区

jounzhang

2018-06-28 19:24

JFinal

2018-06-28 21:50

catch 块中的内部事务放在一个新建的 Thread 中完成即可

因为 connection 是与线程绑定的,新建一个线程就摆脱了外层的事务

JFinal

2018-06-28 21:51

具体做的时候,可以写个工具类,方便创建线程,利用 Java 8 的 lambda 来减少代码量

欲风217

2018-06-29 09:00

我的做法是,直接将数据存到数据库任务表,存成功后返回「已受理」结果。有一个轮循每分钟检查有无任务,有的话执行,执行完打标记,如果有错误记录错误。

要输就输给追求

2018-06-29 17:45

我用jfinal-event 做过类似的事情。就是客户上传excel,上传完成我就异步解析excel,然后把excel解析结果通过微信通知到上传人

jounzhang

2018-07-03 09:23

@欲风217 这个思路是正统,只是这个项目以前不是异步的,是简单重构后这样子的。只是采用了简答的多线程异步处理。

jounzhang

2018-07-03 11:00

@JFinal 新开一个线程拿不到状态了,而且变成异步的,可能会涉及到线程通信。jfinal以后能不能提供一个支持基于连接的事务处理。如:
Connection conn = DbKit.getConfig().getDataSource().getConnection();
Db.tx(new IAtom(conn) {
@Override
public boolean run(Connection newConnection) throws SQLException {
SysTask sysTask = new SysTask();
sysTask.use(newConnection);
sysTask.update();
sysTask.save();
sysTask.update();
return true;
}
});

或者:
Db.tx(new IAtomNew() {
@Override
public boolean run(Connection newConnection) throws SQLException {
SysTask sysTask = new SysTask();
sysTask.use(newConnection);//显示指定连接
sysTask.update();
sysTask.save();
sysTask.update();
return true;
}
});

这样就能随心所欲的处理嵌套逻辑了,默认使用当前线程连接,也可以支持使用新的连接。

JFinal

2018-07-03 11:06

@jounzhang 实现这个比较麻烦

jounzhang

2018-07-03 16:47

@JFinal 确实,考虑一下嘛,将来如果有这个功能就赞了,这个项目遇到这种需求很多次了,现在是通过:

if(Db.tx(...)){
Db.tx(....);
}else{
Db.tx(....);
}

这种方式来处理的。

passion

2020-03-03 19:16

这个问题困扰我几天了,我也是嵌套事务,以为Db.tx一定是开启新事务,结果我Db.tx保存数据入库后,发送记录id到rabbitmq,让mq处理,总是发现明明A服务保存了,B服务的rabbitmq处理消息,总是查不到记录,真的是大坑大坑。。。。

目前临时解决办法就是Db.tx在新线程中执行

passion

2020-03-03 19:17

没想到18年就有人遇到这个问题,到2020年框架都还没解决

passion

2020-03-03 19:20

我一直一度怀疑是我代码的问题,想了3天,看代码,各种debug也没找出问题所在,就在刚刚我debug看Db.tx的源码实现才发现有这个bug,然后就来官网搜嵌套事务的问题,就发现了这篇文章

jounzhang

2020-03-04 15:34

@jfinal 波总,考虑下加个官方的工具方法吧,我也遇到几次这种场景了。

jounzhang

2020-03-04 15:37

@jfinal

给Db.tx(new IAtom() {}});加个兄弟方法Db.tx(new IAtomNew() {}}),唯一的区别是,IAtom使用当前线程的连接,IAtomNew使用一个新的连接。

JFinal

2020-03-04 15:53

Db.txOnNewThread( ...) 行不行?

jounzhang

2020-03-04 16:02

@JFinal 都可,有现成的就可以省几行代码,就喜欢jfinal一贯的简单易学经久耐用的风格

JFinal

2020-03-04 16:24

@jounzhang 计划上一个 Db.txInNewThread(...) @passion

jounzhang

2020-03-04 16:28

@JFinal 名字是不是太长了

热门反馈

扫码入社