(ObjectOptimisticLockingFailureException) 单个事务中的 StaleStateException

(ObjectOptimisticLockingFailureException) StaleStateException in a single transaction

为了避免不必要的干扰,特意简化和删减了以下代码。

我 mysql 数据库 table:

CREATE TABLE `payment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `status` varchar(45) DEFAULT NULL,
  `last_update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

我有 Spring 从控制器调用的事务方法。

@GetMapping("/reproduce_error")
    @Transactional
    public ResponseEntity reproduceErrorOnUpdate(Long id) {
        try {
            Payment payment = paymentDao.getPayment(id); //1st query
            payment.setStatus(payment.getStatus().equals("A") ? "B" : "A");
            paymentDao.findAllByStatus("NOT EXISTING STATUS"); // 2nd query
            payment.setStatus("C");
        } catch (Exception e) {
            return new ResponseEntity<>(e, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity(HttpStatus.OK);

    }

当我调用这个方法时总是出现异常:

org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

但在同一时间(这是最奇怪的事情):

如果我们省略第二个查询(无论如何 returns 为 null!),该方法可以正常工作!

两种方法都在SINGLE事务中执行! (我是这样称呼它们的,所以你可以看到某处没有其他交易

没有不同的线程或 HTTP 调用处理同一个数据库行(根据我在本地计算机上重现的情况)。唯一的区别是,在第一种情况(错误)中,我们在数据库中进行第二次搜索并出现错误(即使第二次搜索 returns 没有),而在第二种情况(成功)中,我们不这样做所以。

我了解(在错误情况下)Hibernate 在执行对 DB 的第二次调用之前刷新更改以支持一致性。但是为什么我们有 StaleStateException,因为我们 DO 处理ONE 事务的所有内容以及其中所做的每个更改都应该对自身可见。

谁能帮我解决这种奇怪的休眠行为并解释为什么会这样。

我已经准备好最小的可复制应用程序并将其放在 bitbucket 上: https://bitbucket.org/gegunov/hibernate_issue

我也在 Hibernate 项目的 Jira 上提了个票:https://hibernate.atlassian.net/browse/HHH-13867

Hibernate 团队帮助了我。事实证明,这个问题是 Hibernate 和 MySql 与刷新期间的精度损失相关的错误的结果。

解决方法之一是将数据库列格式从 TIMESTAMP 更改为(例如)TIMESTAMP(6) 或 DATETIME(6)。