Hibernate事务计数器行为

时间:2015-04-16 18:36:43

标签: java oracle hibernate jpa transactions

我有以下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实现乐观锁定来解决这个问题,但我对此感到有些惊讶。

为什么上述代码会因为更新而丢失更新?

2 个答案:

答案 0 :(得分:2)

你是对的,只有乐观或悲观的锁定可以帮助你,如果你想在数据库中准确保存有效值。在你的情况下,悲观锁定会更好,因为应用程序在竞争环境中工作,但乐观锁定将一次又一次抛出异常。应用程序服务器重启或挂起后,Atomic的所有优点都会丢失。

答案 1 :(得分:0)

Atomic意味着一次只能进行一次操作。除非变量的类型为 long double ,否则java语言规范保证读取或写入变量是原子。 因为变量(long / double)是使用两个独立的操作写入的:一个写入前32位,另一个写入最后32位。这意味着另一个线程可能会读取count的值,并看到中间状态。

使此操作成为原子的一种简单方法是使变量volatile:

private volatile long count;

现在,count永远不会被任何Thread缓存,并确保此变量将始终由主内存读取,并且在并发环境中永远不会丢失您的计数。 / p> 鉴于您不在实体类中使用它,

AtomicLong也可能对您感兴趣。