【问题标题】:Concurrent insert into entity collection并发插入实体集合
【发布时间】:2015-06-23 11:16:35
【问题描述】:

我在FamilyPerson 之间有双向映射:

@Entity class Family {
    @OneToMany(mappedBy = "family", ...)
    Set<Person> persons;
    ...
} 

@Entity class Person {
    @ManyToOne
    Family family;
    ...
}

我的问题是我可以同时从集合中添加和/或删除元素,这会破坏更新中的原子性,因为添加新人会创建 INSERT INTO Person ... 不会与其他插入冲突的语句。 @Version 字段没有帮助,因为我没有将Family 实体更新为表中的行,只是在逻辑上。

我应该怎么做才能只启用集合的原子更新?我曾尝试用LockMode.PESSIMISTIC_WRITE 加载Family,这可以同步Family 本身的更新,但这不允许对Family 的读取进行二级缓存。我更喜欢在数据库中具有谓词约束的乐观事务。

【问题讨论】:

  • 因此你必须使用事务!

标签: sql hibernate jpa


【解决方案1】:

你可以使用OPTIMISTIC_FORCE_INCREMENT:

entityManager.lock(family, LockModeType.OPTIMISTIC_FORCE_INCREMENT);

这种方式会在对应的Family实体实例中检查并递增版本。

编辑

由于您使用的是 L2 缓存,并且出于某种原因(错误、设计决策或只是缺少功能),Hibernate 在强制增量后不会更新 L2 缓存中的版本,您将不得不实际加载实体这把锁让一切正常工作:

entityManager.find(Family.class, id, LockModeType.OPTIMISTIC_FORCE_INCREMENT);

但是,现在您必须注意保持一致,并始终在更新 Family 实体之前以这种方式加载它们(以避免使用过时的版本值,因为 L2 缓存与数据库不同步)。

为了克服这个问题,您可以向Family 实体添加一个人工列,例如lastUpdateTime 或其他东西,并在不使用任何显式锁定的情况下更新Family 实例。然后会进行常规版本检查,并且所有内容都将与 L2 缓存同步。

【讨论】:

  • 这似乎可行,尽管我现在从 DB (H2 1.3.176) 获得了一些锁定超时......一旦我弄清楚发生了什么,我就会将其标记为正确。
  • 谢谢,我看看为什么L2不通知缓存。目前我仍在尝试修复 L2 Infinispan 实现,因为在使用集群缓存时存在一些一致性问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 2011-07-16
相关资源
最近更新 更多