我正在开发一个使用Hibernate的多线程 Java 应用程序。我们得到了一个org.hibernate.StaleObjectStateException
,因为我们有乐观锁定,并且某个实体(我们称之为它Pojo
)从许多应用程序模块中更新,这些模块可能并且已经在竞争条件下发生冲突(我没有设计应用程序)。有问题的代码块如下所示:
Transaction txn = null;
HibernateException ex = null
for(int retryAttempt = 0; retryAttempt < 5; retryAttempt++) {
try {
txn = hibnSessn.beginTransaction();
int pojoID = pojo.getID();
pojo = hibnSessn.get(Pojo.class, pojoID);
pojo.setSomeVar("xyz");
txn.commit();
ex = null;
} catch(HibernateException e) {
if (txn != null)
{
txn.rollback();
}
ex = e;
// Gonna keep retrying
continue;
}
break;
}
并Pojo.hbm.xml
开始这样的事情:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.myproject.Pojo" table="pojo">
<id name="id" column="id" type="int">
<generator class="identity"/>
</id>
<!-- this is used to enforce optimistic locking -->
<version name="dbVersion" column="db_version"/>
...
这段代码的原作者将上述重试循环放入上述重试循环正是因为还有其他模块也在更新Pojo
,如果初始提交由于竞争条件而失败,我们会进行额外的尝试,希望能够在一个干净、不冲突的事务中成功。我不喜欢这种模式,但暂时必须使用它。
我已经能够通过在 line 中断、pojo.setSomeVar("xyz");
从另一个线程/方法更新数据库并继续操作来检测竞争条件,由于乐观锁定、回滚并进入下一次迭代,提交失败并进入 catch的重试循环。
问题在于,在第二次迭代中,该行pojo = hibnSessn.get(Pojo.class, pojoID);
不会pojo
使用来自 DB 的新数据进行刷新,而是从会话中提取过时的对象,从而违背了重试的目的。
另一个有趣的事情是它hibnSessn.get(...)
确实进入了数据库(在正常情况下),因为当我hibnSessn.get
在提交失败之前的第一次迭代之前从另一个线程/方法更改数据时,对象确实会被刷新。只有在提交失败并且事务回滚后,它才会这样做。
我正在寻找在初始提交失败后强制 Hibernate 进入数据库并刷新对象的方法。
更新: 我尝试evict/refresh
在 catch 中刷新对象,但这也导致了StaleObjectStateException
.