2

我在远程网络服务器上有一个订购平台,在我的办公室有一台本地生产机器。两个网络服务器都访问同一个远程 MySQL (InnoDB) 数据库。

我的问题:生产需要几分钟,其中有 2-3 笔交易是开放的。在这个时候,我生成新的发票号码并增加它们。最新的发票编号保存在数据库的 Numbers-Table 中。

public Long getNewInvoiceNumber() {
    Criteria crit = getSession().createCriteria(Numbers.class);
    Numbers n = ((Numbers)crit.uniqueResult());
    Long newNumber = n.getInvoiceNumber() + 1L;
    n.setInvoiceNumber(newNumber);
    return newNumber;
}

现在,当有人在生产过程中保存新订单时,他们正在访问相同的 Numbers-Table 以生成另一个编号(不是发票)。生产处理的所有订单都保存有正确的发票编号。但是,Numbers-Table 不会更新为最新值,并且发票编号与生产前相同。

我了解其中一项事务收到“陈旧表”消息。但是 MySQL / Hibernate / Java 的行为是什么?我想从其中一个事务中得到一个异常,这样我就可以回滚并且没有这种危险的数据库不一致。

编辑: 这是表格的Numbers样子:

id | invoice_number | tag_number
0  | 16533          | 1055

id是主键。我只访问这一行并增加所需的数量。

编辑 2: 好的,我看到这个表结构有点糟糕。我将其更新为:

id             | number
invoice_number | 16533
tag_number     | 1055

现在我可以独立访问每一行。不知道这是否解决了我的问题。

4

2 回答 2

0

有不同的方法可以同时使用数据库,例如使用乐观锁或悲观锁(你应该小心后者,因为它可能会导致延迟和死锁)。

但我想在您的情况下,您必须在某些事务开始后立即更改 Numbers 表,而不是在它结束时。这将确保为发票预留空位。

查看 Hibernate 的 Id Generators 也很有意义,您可以看到关于如何实现价值生成机制的不同想法。

于 2012-09-20T13:19:08.853 回答
0
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;
}

如果使用 MySQL,则必须使用 InnoDB,应确保 Hibernate 将“FOR UPDATE”添加到为crit.uniqueResult()部件生成的 SQL 中。我还会在之后设置一个 Java 断点(停止执行)并手动测试相同的 SQL 查询会导致另一个客户端阻塞。

这更多是为了测试您的 SQL 服务器是否正常并且您的方言设置是否正确,并且该功能基本上适合您。

这会在生成下一个 InvoiceNumber 时强制在 SQL 服务器上进行序列化。

这样,即使 1000 个用户同时尝试开具发票,您也永远不会得到 2 个相同的 InvoiceNumber。

注意: LockOptions 替换 LockMode 以指定有关并发这一方面的模式。请参阅 hibernate 发行说明以获取帮助。

注意:您谈到了持续几分钟的交易。当交易未提交时,其他用户无法生成新的发票编号。也许您需要将其作为嵌套和单独的事务运行。然后,如果由于其他一些处理错误而无法使用该号码,则 InvoiceNumber 中会出现问题漏洞。会计师/会计系统不喜欢发票号码中的漏洞,也不喜欢它们过时的顺序。

相关 MySQL 文档链接https://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

于 2012-09-20T13:19:34.787 回答