以下是the sample project,其中再现了例外情况。
此示例说明了许多并发事务正在修改帐户余额时的问题。帐户可以绑定许多卡实体。交易与订单和最后的订单有关。每个Thread执行如下:
帐户和订单实体的版本为@ javax.persistence.Version。在最后一步中,使用悲观写锁定来锁定帐户实体:
@Override
public Account getLockedAccount(Integer id) {
final Account account = findOne(id);
em.lock(account, LockModeType.PESSIMISTIC_WRITE);
return account;
}
要测试并发访问,请使用JMeter脚本src / main / resources / StressTest.jmx。注意:由于Extra libs的使用,必须将JSON Path extractor安装到JMeter home以运行脚本。通过普通笔记本电脑上的这些特定设置,您可以获得大约10%的TxEnd请求错误:
{
"timestamp":1425407408204,
"status":500,
"error":"Internal Server Error",
"exception":"org.springframework.orm.ObjectOptimisticLockingFailureException",
"message":"Object of class [sample.data.jpa.domain.Account] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [sample.data.jpa.domain.Account#1]",
"path":"/tx/1443/stop/46.4"
}
尽管使用了悲观的写锁定,我仍然得到乐观锁定异常。是否有其他方法可以确保帐户的完整性,而无需为所有更新或同步方法创建任务执行队列?
UPD :使用任务执行程序的工作放在another branch中。 Spring ThreadPoolTaskExecutor
与事务性任务相结合可以解决问题。
答案 0 :(得分:0)
在查找和锁定之间,可能已经修改了Account对象。 你需要在一个声明中做到这一点
EM.find(Account.class,id,LockModeType.PESSIMISTIC_WRITE)