我在单个事务中遇到多个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也会被反转?
答案 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中的注释是不够的。