Eclipselink插入具有0 ID的实体

时间:2013-03-05 14:21:05

标签: java jpa eclipselink

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

基本上,我有两个涉及的实体,让我们简单地称它们为A和B.A有一个急切的(如果这很重要),非级联的多对一单向引用B在数据库中用连接列建模。数据库表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。一切都按预期工作。

然后,有人(I)将关系的级联类型从none更改为merge / persist,因为代码中的其他位置需要这样。我希望旧代码能够正常工作,合并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或零主键。

错误或我的错误?


编辑:詹姆斯要求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);可以解决我的问题。对我来说,为什么调用merge会导致EclipseLink完全失败,尝试插入没有填充id的B实例,这一点并不明显。

1 个答案:

答案 0 :(得分:1)

这很奇怪,B的Id是如何映射的?

似乎合并可能会以某种方式获得B的两个独立实例(因为没有什么可以将它们识别为与它们没有Id相同)。通常不需要merge(),只有分离的对象才需要它,例如使用序列化时(尝试使用persist)。

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