<p>我有一个在网络服务器上运行的 java 项目。 我总是遇到这个异常。<br />
我阅读了一些文档,发现悲观锁定(或乐观,但我读到悲观更好)是防止此异常的最佳方法。<br />
但我找不到任何明确的例子来解释如何使用它。<br />
我的方法是这样的:</p>
<pre><code class="lang-"> @Transactional
public void test(Email email, String subject) {
getEmailById(String id);
email.setSubject(subject);
updateEmail(email);
}
while:
• Email is a Hibernate class (it will be a table in the database)
• getEmailById(String id) is a function that returns an email (this method is not annotated with @Transactional)
• updateEmail(email): is a method that updates the email.
</code></pre>
<p>注意:我使用 Hibernate 进行保存、更新等(例如:session.getcurrentSession.save(email))</p>
<p>通常不建议使用悲观锁定,而且它在数据库端的性能方面非常昂贵。 您提到的问题(代码部分)有几件事不清楚,例如:<br />
• 如果您的代码同时被多个线程访问。<br />
• 您如何创建会话对象(不确定您是否使用 Spring)?<br />
Hibernate<a href="http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/Session.html" target="_blank">会话对象不是线程安全的</a>。因此,如果有多个线程访问同一个会话并试图更新同一个数据库实体,那么您的代码可能会出现如下错误情况。</p>
<p>因此,这里发生的情况是,多个线程尝试更新同一个实体,一个线程成功,当下一个线程提交数据时,它看到它已经被修改,并最终抛出 StaleObjectStateException<br />
编辑:</p>
<p>有一种方法可以在 Hibernate 中使用悲观锁定。<a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-locking" target="_blank">查看此链接</a>。但这一机制似乎存在一些问题。然而,我在 hibernate(<a href="https://hibernate.atlassian.net/browse/HHH-5275" target="_blank">HHH-5275</a>)中发现了一个 bug 。bug 中提到的场景如下:</p>
<p>两个线程正在读取同一个数据库记录; 其中一个线程应该使用悲观锁定,从而阻塞另一个线程。 但是两个线程都可以读取导致测试失败的数据库记录。<br />
这与您面临的情况非常接近。 如果这不起作用,请尝试此操作,我能想到的唯一方法是使用<a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querysql.html" target="_blank">本机 SQL 查询</a>,您可以使用 SELECT FOR UPDATE 查询在 <a href="http://www.postgresql.org/docs/8.2/static/sql-select.html#SQL-FOR-UPDATE-SHARE" target="_blank">postgres 数据库中实现悲观锁定</a>。</p>