我们有一个遗留的 Spring Hibernate 应用程序,它利用基于 XML 的 Hibernate 配置。间歇性地,当并发调用一个操作时,我们会遇到
javax.persistence.OptimisticLockException:批量更新返回 更新 [0] 中的意外行计数;实际行数:0;预期的: 1.
据我所知,如果多个事务访问同一个表,那么在向数据库发出并发请求时,我们通常会遇到 OptimisticLockException。然而,就我而言,它是在单个事务中。让我分享映射和代码以更好地解释。
用户.hbm.xml:
<class lazy="false" name="UserBO" table="USER">
<id name="id" type="java.lang.Long" column="ID" unsaved-value="null">
<generator class="native"/>
</id>
<many-to-one name="addressSet" column="ADDRESS_SET" class="AddressSetDO"/>
</class>
地址集.hbm.xml:
<class lazy="false" name="AddressSetDO" table="ADDRESS_SET_TABLE">
<id name="id" type="java.lang.Long" column="ID" unsaved-value="null">
<generator class="native"/>
</id>
<version column="HIB_VERSION" name="HibVersion"/>
<set name="address" inverse="true" lazy="false" cascade="delete">
<key column="ADDRESS_SET_ID" />
<one-to-many class="AddressDO"/>
</set>
</class>
地址.hbm.xml:
<class lazy="false" name="Address" table="ADDRESS">
<id name="id" type="java.lang.Long" column="ID" unsaved-value="null">
<generator class="native"/>
</id>
<version column="HIB_VERSION" name="HibVersion"/>
<many-to-one name="addressSet" not-null="true" class="AddressSetDO" index="ADDRESSSET" inverse="true" >
<column name="ADDRESS_SET_ID" not-null="true" /> </many-to-one> </class>
代码流程:从主类来看(由于逻辑复杂,我们尝试在事务内部调用3次save方法): 服务等级:
Transaction start
// code logic
dao.save(data);
// code logic
dao.save(data);
// code logic
dao.save(data);
Transaction end
Dao类(即使我们有映射,我们还是手动保存映射表):
saveOrUpdate(userBo); //userBo
if(userBo.getAddressSet() != null)
saveOrUpdate(userBo.getAddressSet()); //AddressSet
saveOrUpdate(userBo.getAddressSet().getAddress()); //Address
我的 saveOrUpdate 代码是:
void saveOrUpdate(obj) {
try {
hibernateSession().saveOrUpdate(obj);
checkReadOnlySession();
hibernateSession().flush();
} catch (PersistenceException e) {
throw e;
}
}
注意:这个逻辑已经存在很多年了,试图改变它会带来巨大的风险。
现在,如果我继续使用单独的数据调用该功能,我会间歇性地遇到 saveOrUpdate(userBo.getAddressSet()) 的以下问题:
javax.persistence.OptimisticLockException:批量更新返回 更新 [0] 中的意外行计数;实际行数:0;预计:1 在 org.hibernate.internal.ExceptionConverterImpl.wrapStaleStateException(ExceptionConverterImpl.java:212) 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:86) 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) 在 org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1434) 在 org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1414)
但是当我启用 show_sql 查看脚本时,问题不会发生。另外,当我将持久性延迟 10 毫秒时,我无法重现该问题:
Thread.sleep(10);
saveOrUpdate(userBo.getAddressSet());
这让我相信问题不在于多个事务修改相同的数据,而在于事务中对同一个表的多次调用。
问题:
处理这种情况的最佳方法是什么? 我认为这应该涉及重试该机制。在这种情况下,最好的方法是什么?我们可以在同一事务中重试(第一种方法)或从新事务重试(意味着从头开始重试操作)吗?
如有任何帮助,我们将不胜感激。
重试业务事务作为 OptimisticLockException 的通用恢复策略仅适用于简单的情况。复杂的用例通常需要合并,因此用户界面需要调整