Hibernate 事务计数器行为
Hibernate Transaction Counter Behaviour
我有以下 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 实现乐观锁定来解决这个问题,但我有点惊讶这是必需的。
为什么上面的代码即使有事务也会丢失更新?
原子意味着一次只有一个操作。 java 语言规范保证读取或写入变量是原子的 除非变量是 long 或 double[=26= 类型]。
因为变量 (long/double) 是使用两个单独的操作写入的:一个写入前 32 位,第二个写入最后 32 位。这意味着另一个线程可能会读取 count
的值并查看中间状态。
使此操作成为原子操作的一种简单方法是使变量成为易变的:
private volatile long count;
现在,count
永远不会被任何 Thread
缓存,并且可以确保这个变量将始终被主内存读取,并且在并发环境中永远不会丢失您的计数.
AtomicLong 您可能也会感兴趣,因为您不会在实体中使用它 class。
你是对的,如果你想在数据库中准确保存有效值,只有乐观或悲观锁定可以帮助你。在您的情况下,悲观锁定会更好,因为应用程序在竞争环境中工作,但乐观锁定会一次又一次地抛出异常。应用服务器重启或挂起后,Atomic 的所有优势都将丢失。
我有以下 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 实现乐观锁定来解决这个问题,但我有点惊讶这是必需的。
为什么上面的代码即使有事务也会丢失更新?
原子意味着一次只有一个操作。 java 语言规范保证读取或写入变量是原子的 除非变量是 long 或 double[=26= 类型]。
因为变量 (long/double) 是使用两个单独的操作写入的:一个写入前 32 位,第二个写入最后 32 位。这意味着另一个线程可能会读取 count
的值并查看中间状态。
使此操作成为原子操作的一种简单方法是使变量成为易变的:
private volatile long count;
现在,count
永远不会被任何 Thread
缓存,并且可以确保这个变量将始终被主内存读取,并且在并发环境中永远不会丢失您的计数.
AtomicLong 您可能也会感兴趣,因为您不会在实体中使用它 class。
你是对的,如果你想在数据库中准确保存有效值,只有乐观或悲观锁定可以帮助你。在您的情况下,悲观锁定会更好,因为应用程序在竞争环境中工作,但乐观锁定会一次又一次地抛出异常。应用服务器重启或挂起后,Atomic 的所有优势都将丢失。