下面的代码被有意简化和删节,以避免不必要的干扰。
我有带有表的mysql数据库:
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 Transactional方法。
@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
但同时(这是最奇怪的事情):
如果省略第二个查询(无论如何返回null!),则该方法正确运行!
两个方法都在SINGLE事务中执行!(这就是我对它们的称呼,因此您可以看到某处没有其他交易)
没有相同的数据库行使用不同的线程或HTTP调用(根据我在本地计算机上重现的内容)。唯一的区别是,在第一种情况(错误)中,我们在数据库中进行了第二次搜索,但有一个错误(即使第二次搜索未返回任何内容),在第二种情况(成功)中,我们没有这样做。
我知道(在错误情况下)Hibernate在执行第二次对DB的调用之前刷新更改以支持一致性。但是为什么在我们DO处理它的所有<< [ONE事务以及其中所做的所有更改都对自己可见的情况下又出现StaleStateException。
有人可以帮助我解决这种奇怪的休眠行为,并解释为什么会发生这种情况。我还在Hibernate项目的吉拉(Jira:https://hibernate.atlassian.net/browse/HHH-13867]出票了>
Hibernate团队为我提供了帮助。事实证明,此问题是由于刷新期间Hibernate和MySql错误与精度损失相关的结果。
解决方法之一是将数据库列格式从TIMESTAMP更改为(例如TIMESTAMP(6)或DATETIME(6)。