OptimisticLockException与Spring引导下的并发JPA更新

OptimisticLockException与Spring引导下的并发JPA更新,jpa,optimistic-locking,pessimistic-locking,Jpa,Optimistic Locking,Pessimistic Locking,这里是例外重现的地方 此示例说明了当许多并发事务正在修改帐户余额时的问题。帐户可以绑定多个卡实体。交易与订单和最后一次相关。每个线程执行如下操作: 客户端根据给定的卡哈希id请求“/order/{hashId}”以获取第一个可用订单 客户端为给定订单启动新的发送-'/tx/{orderId}/start' 客户端完成tx-'/tx/{txId}/stop/{amount}',其中tx金额从帐户余额中减去 实体锁定 帐户和订单实体的版本为@javax.persistence.Version。在最后

这里是例外重现的地方

此示例说明了当许多并发事务正在修改帐户余额时的问题。帐户可以绑定多个卡实体。交易与订单和最后一次相关。每个线程执行如下操作:

  • 客户端根据给定的卡哈希id请求“/order/{hashId}”以获取第一个可用订单
  • 客户端为给定订单启动新的发送-'/tx/{orderId}/start'
  • 客户端完成tx-'/tx/{txId}/stop/{amount}',其中tx金额从帐户余额中减去
  • 实体锁定 帐户和订单实体的版本为@javax.persistence.Version。在最后一步中,帐户实体被悲观写锁锁定:

    @Override
    public Account getLockedAccount(Integer id) {
        final Account account = findOne(id);
        em.lock(account, LockModeType.PESSIMISTIC_WRITE);
        return account;
    }
    
    测试 要测试并发访问,请使用脚本src/main/resources/StressTest.jmx。注意:由于使用了,必须安装到JMeter home才能运行脚本。在普通笔记本电脑上使用这些特定设置,您可以获得TxEnd请求约10%的错误:

    {  
    "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:与任务执行器的变通操作被置于。Spring
    ThreadPoolTaskExecutor
    与事务性任务相结合可以解决此问题。

    在查找和锁定之间,帐户对象可能已被修改。 你需要用一句话来表达


    EM.find(Account.class,id,LockModeType.悲观写入)

    在使用EntityManager之前,我尝试了@Lock注释:
    @Lock(LockModeType.悲观写入)Account findOne(整数)。可以找到以前版本的完整实现。在Spring SimpleParepository中,当提示是当前LockModeType时,使用相同的等价物
    em.find(domainType,id,type,Hits)