【问题标题】:Hibernate Transaction Counter Behaviour休眠事务计数器行为
【发布时间】:2015-06-23 08:44:30
【问题描述】:

我有以下 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 oracle hibernate jpa transactions


    【解决方案1】:

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

    使这个操作原子化的一个简单方法是使变量可变:

    private volatile long count;
    

    现在,count 永远不会被任何 Thread 缓存,并且可以确保此变量将始终由主内存读取,并且在并发环境中永远不会丢失您的计数。

    AtomicLong 可能你也会感兴趣,因为你没有在实体类中使用它。

    【讨论】:

    • 我不担心这在 Java 级别是原子的。它在数据库级别上不是原子的,这就是我现在正在调查的内容。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-19
      • 2016-10-30
      • 2015-08-30
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      • 2010-11-14
      • 1970-01-01
      相关资源
      最近更新 更多