【发布时间】:2018-03-19 14:58:08
【问题描述】:
spring 环境有一个大现象 不然我大错特错了。 但是默认的spring @Transactional 注释不是ACID,而只是缺乏隔离的ACD。这意味着如果你有方法:
@Transactional
public TheEntity updateEntity(TheEntity ent){
TheEntity storedEntity = loadEntity(ent.getId());
storedEntity.setData(ent.getData);
return saveEntity(storedEntity);
}
如果 2 个线程以不同的计划更新进入会发生什么。他们都从数据库加载实体,他们都应用自己的更改,然后第一个被保存并提交,当第二个被保存并提交时,第一个更新丢失。真的是这样吗?使用调试器,它就是这样工作的。
【问题讨论】:
-
这取决于数据库和数据库的默认事务行为。使用
REPEATABLE_READ或SERIALIZED会起作用,但这会对性能产生很大影响。这也是为什么 JPA 提供程序(如 Hibernate)具有乐观锁定功能的原因(基本上是更新中包含的version或timestamp列)。当 2 个线程执行更新时,1 会因为版本号不匹配而失败。 -
saveEntity是不必要的。如果TheEntity是一个托管实体(因为它应该是,考虑到该方法是事务性的),对它所做的任何更改都将在事务结束时刷新到数据库中。正如@M.Deinum 所说,乐观锁定机制应该可以防止任何丢失的更新。 -
yeye 我知道持久分离等状态我只是为了可读性而添加它,但这与问题无关。问题是......为什么 sprint @Transactional 的行为不像每个人所期望的那样 - ACID。我周围所有使用它多年的开发人员都不敢相信它是这样工作的,但确实如此。丢失的更新正在发生。不是吗?
-
一种避免的方法是隔离 = ISOLATION.SERIALIZABLE,但它会抛出异常,所以它不是那么直接,另一种是乐观锁定,但也需要一些更改。
-
您不会丢失数据。你有两个更新。您的实体不能处于两种状态。我认为您真正想知道的是并发是如何工作的。