在同一个交易中多个Eclipselink合并需要Flush()吗?

时间:2011-12-16 19:48:29

标签: oracle10g eclipselink entitymanager

我在单个事务中遇到多个EntityManager.merge()调用的问题。这是使用Oracle数据库。这两个对象都不存在。实体:

public class A {
    @Id
    @Column("ID")
    public Long getID();

    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();
}

合并代码如下所示:

@Transactional
public void create(Object A, Object B) {
    Object A = entitymanager.merge(A);
    B.setId(A.getId());
    entitymanager.merge(B);
}

对象A的ID是通过序列生成的,它在B上正确设置。查看日志,在调用B上的合并之前调用A上的合并。从A到B有一个@OneToOne映射。但是,在实际提交的方法结束时,它会尝试在B上执行INSERT,然后在A上执行INSERT,抛出{{1因为“找不到父键”。

如果我在第二次合并之前添加IntegrityConstraintViolation,它可以正常工作。

entitymanager.flush()

但是,@Transactional public void create(Object A, Object B) { Object A = entitymanager.merge(A); entitymanager.flush(); B.setId(A.getId()); entitymanager.merge(B); } 是一项昂贵的操作,不需要。所有这些都应该在同一个事务中发生(flush()的默认传播是@Transactional)。

知道为什么没有Propagation.REQUIRED这不起作用,为什么即使A上的合并发生在B上的合并之前,COMMIT上的实际INSERT也会被反转?

3 个答案:

答案 0 :(得分:0)

如果实体A和B没有关系(即@OneToOne,@ OneToMany,...),则持久性提供程序无法计算正确的插入顺序。 IIRC EclipseLink在将SQL语句发送到数据库时不使用对象创建顺序。

如果您不想使用flush(),只需将约束设置为延迟。

答案 1 :(得分:0)

正如Frank所提到的,您显示的代码没有设置A-> B关系,因此提供者无法知道在B之前需要插入此B对象。其他关系可能会导致它认为一般来说A需要先插入。

可以对某些数据库执行延迟约束,并且指的是将数据库设置为推迟约束处理直到事务结束。如果您推迟或删除约束,则可以查看正在生成的SQL是否正确,或者是否存在错过的代码和映射的其他问题。

答案 2 :(得分:0)

看起来合并是按字母顺序排列的(至少是一种可能性),除非有双向@OneToOne注释。

此前:

public class A {
    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();
}

现在:

public class A {
    @OneToOne(targetEntity = B.class)
    @JoinColumn("ID")
    public B getB();
}

public class B {
    @Id
    @Column("ID")
    public Long getID();

    @OneToOne(targetEntity = A.class)
    @JoinColumn("ID")
    public A getA();
}

对于我正在做的事情,B有办法获得A并不重要,但我仍然不明白为什么A中的注释是不够的。