JFinal的事务级别

service级别的事务,是否在一个按钮点击开始,事务开始,直到这个方法结束,只提交一次事务给数据库。不管这个方法里面操作了多少次表,或者多少张表。

PS:我遇到的问题是,我有一个共通的各种单号自动生成工具类,现在两个不同的用户在同一秒中,做了相同的操作,导致取到了相同的单号。

评论区

JFinal

2019-05-24 15:04

service 事务的边界正如你需求的那样,从你点击开始直到节束,无论中间有多少步

你碰到的问题,很可能是因为事务级别不够引起的,对于 mysql 来说 jfinal 比较高版本的事务级别是:
Connection.TRANSACTION_REPEATABLE_READ;

这个值是:4, 对于一般的应用是够用的

oracle 下面值是: 2

在使用事务的时候,有一件事情要特别注意,如果你是将数据库的值先读到内存,然后改这个值,再存到内存,那么事务级别是要相应提高的,例如:
int cash = Db.queryInt("select cash from account where id = ?", 123);
cash = cash + 100;
Db.update("update account set cash = ? where id = ?", cash, 123);

如果你的更新操作是用 sql 实现,直接在数据库中实现的,那么事务级别可以更低,例如:
Db.update("update account set cash = cash + 100 where id = ? ", 123);

注意区别上面两种用法,前面的用法对数据的操作是在你的应用中, 后面的用法是在 mysql 数据库中,这两种用法对事务级别的要求是不同的,前者起码要锁住 id 值为 123 的行,如果更新范围大需要锁表,这个锁行、锁表就是与事务级别有关的,事务级别越高,锁的范围就越大

hb963724769

2019-05-25 10:13

@JFinal 我数据库加上乐观锁了,但是对于这个取单号的,为了用户不用再点击,我单独给这个单号自动生成工具,加上悲观锁。

hb963724769

2019-05-25 10:55

@JFinal 我又发现我的乐观锁好像不好用,不知道是不是用错了。
数据库事务级别是REPEATABLE-READ,我在方法最后一行断点,然后让两个用户先后点击出库,两个订单是同一个商品,发现都正常出库了,我理解的应该是,第一个事务提交后,第二个提交时候应该是版本号不一致,会报错才对啊。
//取出库存
PsiInv dbInv = psiInvDao.findById(dbDetail.getLong("invId"));
//乐观锁
Integer invUpdateCount = dbInv.getUpdateCount();

dbInv.setInv(dbInv.getInv().subtract(dbDetail.getBigDecimal("qty")))
//乐观锁
.setUpdateCount(invUpdateCount)
.setUpdatePage(res.get("menu.billDocPrint"))
.update();

JFinal

2019-05-25 11:20

@hb963724769 特别注意一个事: 数据读到内存,进行改变,然后再存入数据库,即便这个过程处在事务之中,也会出现幻读,除非你将事务级别调到最高级 Connection.TRANSACTION_SERIALIZABLE

但最高级的事务级别对性能影响较大,一般不建议这么用,而是避免读数据再操作,用 sql + para 的方式让数据库来操作数据

hb963724769

2019-05-25 11:38

@JFinal 也就是说我现在这种乐观锁控制不了并发对吗?我先读再更新的原因是,我需要未更新前的数据,后面需要根据这个旧数据计算其他的东西。

我现在在改成select * from XXXXX lock in share mode 加共享锁,让他等待上一进程更新完成,再读数据,像悲观锁!

逍遥一生

2019-05-30 16:54

波总说到了重点,你更新乐观锁的版本号的地方本身就可能出现并发情况,除非你在这块代码上加上同步锁,保证同一时刻只有一个线程可以更新乐观锁的版本号。或者就是直接用sql的update去操作乐观锁的版本号,因为update语句本身就会使用行锁来避免并发的情况。

热门反馈

扫码入社