我有以下JPA实体:
@Entity(name="metrics")
public class Metrics {
@Id
private String metricId;
@Column
private long count;
public Metrics() {
count = 0;
}
我试着像这样原子地更新它:
//Begin transaction
Metrics result = em.find(Metrics.class, id);
if (result == null) {
result = new Metrics();
result.metricId = id;
result.count++;
em.persist(result);
} else {
result.count++;
em.merge(result);
}
//Commit transaction
然而出于某种原因,这似乎并没有使更新成为原子,而我最终会在并发环境中丢失更新。我可以通过使用@Version
使用Hibernate实现乐观锁定来解决这个问题,但我对此感到有些惊讶。
为什么上述代码会因为更新而丢失更新?
答案 0 :(得分:2)
你是对的,只有乐观或悲观的锁定可以帮助你,如果你想在数据库中准确保存有效值。在你的情况下,悲观锁定会更好,因为应用程序在竞争环境中工作,但乐观锁定将一次又一次抛出异常。应用程序服务器重启或挂起后,Atomic的所有优点都会丢失。
答案 1 :(得分:0)
Atomic意味着一次只能进行一次操作。除非变量的类型为 long 或 double ,否则java语言规范保证读取或写入变量是原子。
因为变量(long / double)是使用两个独立的操作写入的:一个写入前32位,另一个写入最后32位。这意味着另一个线程可能会读取count
的值,并看到中间状态。
使此操作成为原子的一种简单方法是使变量volatile:
private volatile long count;
现在,count
永远不会被任何Thread
缓存,并确保此变量将始终由主内存读取,并且在并发环境中永远不会丢失您的计数。 / p>
鉴于您不在实体类中使用它,
AtomicLong也可能对您感兴趣。