【问题标题】:Eclipselink inserting entity with 0 IDEclipselink 插入 ID 为 0 的实体
【发布时间】:2013-03-05 14:21:05
【问题描述】:

我刚刚发现 EclipseLink(2.3.0 和 2.4.1)有一个相当奇怪的问题,只是想问是否有人可以确认这是一个错误,而不仅仅是我遗漏了一些明显的东西......

基本上,我涉及两个实体,让我们简单地称它们为 A 和 B。A 有一个渴望(如果重要的话),对 B 的非级联多对一单向引用,该引用在数据库中使用连接列建模. DB表A包含列ID(PK)、B_ID(FK到B.ID)等,表B包含列ID等。

现在,我有一个 As 列表,应该使用对新 B 实例的引用进行更新。在伪代码中类似于:

for(A a : list) {
    // using some criteria, check if perhaps a matching B
    // entity is already found in the database
    B b = perhapsGetExistingBFromDatabase();

    if(b == null) {
        // no existing B was found, create a new
        b = new B();
        // set values in B
        b.setFoo(4711),
        b.setBar(42);

        // save B
        em.merge(b);
    }

    // set reference from A to B ...
    a.setB(b);
    // ... and update A
    em.merge(a);
}

由于引用是非级联的,因此需要合并 b 和 a。一切都按预期进行。

然后,有人(我)将关系的级联类型从无更改为合并/持久,因为代码中的其他地方需要这样做。我希望旧代码能够工作,合并 b 并不是真正需要的,恕我直言不应该受到伤害吗?一个简短的测试确认它仍然有效,插入了新的 B 实体并相应地更新了 A。

但是,它仅在列表中只有一个 A 实体时才有效。第二次运行循环会导致 EclipseLink 自动刷新会话,因为perhapsGetExistingBFromDatabase 执行“SELECT .. FROM B”,缓存了一个合并的 B 实体,它希望数据库表是最新的。使用代码中的 FINEST 日志记录级别和断点,我可以验证 EclipseLink 确定需要为新的 B 实体生成一个 id,它调用序列生成器并在实体的正确字段中设置 id。尽管如此,EclipseLink 仍会调用类似于以下的 SQL 语句:

INSERT INTO B (ID, ...) VALUES(0, ...);
UPDATE A SET B_ID = 0 WHERE ID = ...;

生成的 id 在某处丢失,EclipseLink 尝试创建一个 id=0 的新实体。数据确实是无效的,后来 EclipseLink 也抛出了 PersistenceException: Null or zero primary key meet in unit of work clone。

错误还是我的错误?


编辑:James 要求提供 B.ID 的映射:

@Id
@GeneratedValue(generator = "sq_idgen", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "sq_idgen", sequenceName = "...", allocationSize = 100)
@Column(name = "id")
protected long id;

请注意,删除不必要的 em.merge(b); 可以解决我的问题。对我来说,为什么调用合并会导致 EclipseLink 完全失败,试图插入一个没有填充 id 的 B 实例,这对我来说并不明显。

【问题讨论】:

    标签: java jpa eclipselink


    【解决方案1】:

    奇怪,B的Id是怎么映射的?

    似乎合并后的 B 可能会以某种方式获得两个单独的实例(因为没有任何东西可以将它们标识为相同,因为它们没有 ID)。并不是说 merge() 通常不需要,它仅对分离的对象需要,例如在使用序列化时(尝试使用 persist 代替)。

    为避免刷新,您可以将 EntityManager 或持久性单元中的 flushMode 设置为 COMMIT 而不是 AUTO。

    【讨论】:

    • 我已将 id-mapping 添加到我原来的问题中。在这种情况下实际上需要合并 b,因为它是一个新对象。如果我坚持它,它的 id 不会立即更新,我也无法将它与现有的实例连接起来。在这种情况下,自动刷新模式也是正确的,因为我可能会创建 B 的新实例,perhapsGetExistingBFromDatabase 可以在以后的循环迭代中找到它。
    猜你喜欢
    • 2017-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-01
    • 2021-05-17
    • 1970-01-01
    相关资源
    最近更新 更多