我有一个包含两个表的数据库(实际上更多,但在这种情况下无关紧要)。其中一个对另一个有外键引用。这些可能是映射:
@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;
...
}
我有一个中间数据结构,其中包含A
和B
之间关系的信息,在我将数据保存在活动事务中之前,我会迭代它,检查是否A
或B
已存在于数据库中或未使用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)生成了更新语句以增加版本列。
问题:为什么?我怎么能阻止这个?为了避免这种情况,我应该如何更改我的数据持久化方法?感谢。
答案 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);