在我的办公室里,我有一个远程网络服务器上的订购平台和一个本地生产机器。两个the服务器都访问同一个远程MySQL (InnoDB)数据库。
我的问题是:生产需要几分钟,其中2-3个交易是开放的。在这段时间里,我生成新的发票号码并增加它们。最近的发票编号保存在数据库中的数字-表中。
public Long getNewInvoiceNumber() {
Criteria crit = getSession().createCriteria(Numbers.class);
Numbers n = ((Numbers)crit.uniqueResult());
Long newNumber = n.getInvoiceNumber() + 1L;
n.setInvoiceNumber(newNumber);
return newNumber;
}现在,当某人在生产过程中保存一个新订单时,他们访问的是相同的数字--表以生成另一个号码(而不是发票)。生产过程中处理的所有订单都以正确的发票编号保存。然而,数字-表没有更新最新的价值和发票号码保持不变的生产前。
据我所知,其中一个事务得到了一条“陈旧表”消息。但是MySQL / Hibernate / Java的行为是什么呢?我想从其中一个事务中获得一个异常,这样我就可以回滚,并且不会有这种危险的数据库不一致。
编辑: --这是表Numbers的样子:
id | invoice_number | tag_number
0 | 16533 | 1055id是主键。我只访问这一行并增加所需的编号。
编辑2 :好的,我看到这个表结构有点糟糕。我将其更新如下:
id | number
invoice_number | 16533
tag_number | 1055现在我可以独立地访问每一行了。但不知道这是否解决了我的问题。
发布于 2012-09-20 13:19:34
public Long getNewInvoiceNumber() {
Criteria crit = getSession().createCriteria(Numbers.class);
crit.setLockMode(LockMode.PESSIMISTIC_WRITE); // LINE ADDED
Numbers n = ((Numbers)crit.uniqueResult());
Long newNumber = n.getInvoiceNumber() + 1L;
n.setInvoiceNumber(newNumber);
getSession().update(n); // LINE ADDED
return newNumber;
}如果使用InnoDB,则必须使用MySQL,您应该确保Hibernate在为crit.uniqueResult()部件生成的SQL中添加"FOR UPDATE“。我还会在(停止执行)之后设置一个Java断点,并手动测试相同的SQL查询会导致另一个客户机阻塞。
这将更好地测试您的SQL服务器是否正常,您的方言设置是否正确,基本上该特性适合您。
这将强制在生成下一个InvoiceNumber时在server上进行序列化。
这样,即使1000个用户同时尝试开发票,也永远不会得到相同的2 InvoiceNumber。
注意: LockOptions取代了LockMode来指定关于并发的这一方面的模式内容。有关帮助,请参阅hibernate发布说明。
注:你谈到的交易持续几分钟。当事务未提交时,其他任何用户都无法生成新的发票号。也许您需要将其作为一个嵌套和独立的事务运行。如果由于其他处理错误而未能使用该数字,则InvoiceNumber中会出现问题漏洞。会计/会计系统不喜欢发票编号上的漏洞,也不喜欢过时的顺序。
相关MySQL文档链接https://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
发布于 2012-09-20 13:19:08
并发处理数据库的方法有多种,例如使用乐观锁或悲观锁(应该小心后者,因为它可能导致延迟和死锁)。
但我想,在您的情况下,您必须在某些事务开始后立即更改Number表,而不是在事务结束时更改。这将确保保留发票的时隙。
查看Hibernate的Id生成器也是有意义的,您可以看到如何实现价值生成机制的不同想法。
https://stackoverflow.com/questions/12512911
复制相似问题