2012-09-20 39 views
2

我在远程Web服务器和本地生产机器上有一个订购平台,位于我的办公室。 两个Web服务器都访问相同的远程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不会更新为最新值,并且发票号码与生产之前保持一致。

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

编辑: 这是表Numbers的样子:

id | invoice_number | tag_number 
0 | 16533   | 1055 

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

编辑2: 奥基,我看到这个表结构有点不好。 我把它更新为:

id    | number 
invoice_number | 16533 
tag_number  | 1055 

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

+0

贵Numbers.class表只能有1排过的?查看setLockMode()/ setLockOptions()的API。您需要SELECT ... FOR UPDATE以确保没有其他用户正在做同样的事情。你也需要getSession()。update(n);对 ? –

+0

是的,截至目前只有1行。我以为数据库有自己的自动锁定模式,我想知道如果有2个用户同时更改行的行为。 – rotsch

+0

两个独立用户可以从两个不同的事务同时查看相同或旧数据。要在相对较少的情况下停止SELECT ... FOR UPDATE存在。我会更多地研究这个话题,并用命令行SQL客户端测试理论。 –

回答

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,你应该确保休眠被添加“FOR UPDATE”为crit.uniqueResult()部分生成的SQL。我还会在(停止执行)之后立即设置一个Java断点,并手动测试相同的SQL查询会导致另一个客户端阻塞。

这是更多的测试你的SQL服务器是否理智,你的方言设置正确,基本上这个功能适合你。

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

这样,即使1000个用户同时尝试制作发票,您也永远不会得到2个发票号码。

注意:LockOptions将替换LockMode以指定有关并发性方面的模式。请参阅休眠版本说明以获得帮助。

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

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

0

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

但我想你的情况下,你必须改变数字表后,一些交易开始,而不是结束。这将确保发票的插槽被保留。

同样有意义的看看Hibernate的Id生成器,你可以看到如何实现价值生成机制的不同想法。