@Version列会更新,即使从未调用过persist

时间:2011-04-10 08:54:46

标签: java database jpa persistence jpa-2.0

我有一个包含两个表的数据库(实际上更多,但在这种情况下无关紧要)。其中一个对另一个有外键引用。这些可能是映射:

@Entity @Table public class A {
   @Id public String id;
   @Version public int version;
   @OneToMany(targetEntity = B.class,
              mappedBy = "a",
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY)
   public Collection<B> bs;

   ...
}

@Entity @Table public class B {
   @Id public String id;
   @Version public int version;
   @ManyToOne(targetEntity = A.class,
              cascade = CascadeType.ALL,
              fetch = FetchType.LAZY)
   public A a;

   ...
}

我有一个中间数据结构,其中包含AB之间关系的信息,在我将数据保存在活动事务中之前,我会迭代它,检查是否AB已存在于数据库中或未使用EntityManager.find(Class<T>, Object)

// get an entity manager and begin transaction
for (C c : cs) {
    A a = em.find(A.class, c.getAId()); // an `A` doesn't exist in the database
    if (null == a) {                    // on first iteration so a new one will
        a = new A();                    // be created
        a.id = c.getAId();              // however in a 100 `C` there are 4-5
    }                                   // unique `A` exists

    B b = em.find(B.class, c.getBId()); // a `B` generally doesn't exist in
    if (null == b) {                    // the database, but the opposite
        b = new B();                    // could happen often
        b.id = c.getBId();
        b.a = a;
        a.bs.add(b);
    }       

    if (!em.contains(b))                // if a `B` was found in the database
      em.persist(b);                    // persist won't execute here
}
// commit transaction

这很好用:新数据会被持久存在,而现有的数据则没有。

然而!无论for-each循环中发生什么,我的数据库 总是 中的版本列都会得到更新。我已经采集了一组样本数据,将其保存在我的空表中(版本为0);我已经采用了同样的一组示例数据并再次保留它(EntityManager.persist(Object)从未被调用过)并且版本字段(在A中)得到了更新,但我无法想象为什么。

我知道即使EntityManager.persist(Object)从未被调用,我的持久性提供程序(Hibernate)生成了更新语句以增加版本列。

问题:为什么?我怎么能阻止这个?为了避免这种情况,我应该如何更改我的数据持久化方法?感谢。

2 个答案:

答案 0 :(得分:3)

我认为这里发生的事情是你将lock mode设置为OPTIMISTIC_FORCE_INCREMENT。如果您将其设置为纯OPTIMISTIC,那么您将看不到更新。对行为的javadoc解释是精确但相当密集的;还有blog post可能有帮助。

我认为OPTIMISTIC_FORCE_INCREMENT的重点是保护数据库的一致性。想象一下,你有两个As,a1和a2,每个都有一个B.两个线程加载它们。线程1设置a1有两个B,a2有一个B(现状)。线程2将a1设置为具有一个B(现状)而a2设置为具有两个B。线程1在线程2之前提交。使用OPTIMISTIC_FORCE_INCREMENT,线程2将获得异常,因为对象已经被线程1修改。使用OPTIMISTIC,线程2将成功,因为每个线程只会碰到它正在添加的对象上的版本一个B到。如果线程2成功,数据库现在表示一个状态,即所提交的线程都没有 - a1和a2都有两个B。

现在,可能是这种不一致 - 一种隐含的状态合并 - 是可以接受的,而且确实是你想要的。但JPA不能假设一般情况下,因此OPTIMISTIC_FORCE_INCREMENT可用。我不知道它是否是默认值(我找不到任何文档说明,但这样做是明智的),或者如果你在其他地方明确地设置它。

对于解决方案,在根本没有修改的情况下,您可能根本就不提交会话。如果您正在处理某些对象被修改而某些对象未被修改的情况,则可以在其自己的会话中处理每个A对象。或者您可以使用OPTIMISTIC锁定。如果我是对的,那就是!

答案 1 :(得分:0)

 B b = em.find(B.class, c.getBId()); // a `B` generally doesn't exist in      
 if (null == a) {  

我想你在发布这两行代码时犯了一个小错误,if条件中的变量应该是b而不是a。如果是这种情况,则行下面会导致更新版本字段。

 a.bs.add(b);