Eclipselink与已连接对象合并/持久化

时间:2012-02-06 19:08:55

标签: java jpa insert eclipselink

我有两个实体,A和B.当我合并A时,我也希望B合并 - 所以我从不对B做操作,只是总是在A上操作.B对象可以通过A对象访问,但反之亦然(如果可能的话)。 A和B共享相同的主键字段,也就是说,B的主键字段是A的主键的外键。但是,我终于得到了INSERT查询的点。按正确的顺序,但因为它没有绑定查询参数中的ID,所以它会抛出InsertNullViolation,因为未设置主键。有谁知道如何让EclipseLink绑定像B这样的连接对象的主键?

public class A {
    ...
    @Id
    @Column(name = "A_ID")
    @SequenceGenerator(...)
    @GeneratedValue(...)
    public Long getA_ID();

    @OneToOne(mappedBy = "a", targetEntity = B.class)
    public B getB();
    ...
}

public class B {
    ...
    @Id
    public Long getA_ID();

    @MapsId
    @OneToOne(targetEntity = A.class)
    @JoinColumn(name="A_ID")
    public A getA();
    ...
}

上面的设置没有在B上设置主键,因此抛出InsertNullViolation尝试插入B。

public class A {
    ...
    @Id
    @Column(name = "A_ID")
    @SequenceGenerator(...)
    @GeneratedValue(...)
    public Long getA_ID();

    @OneToOne(mappedBy = "a", targetEntity = B.class)
    public B getB();
    ...
}

public class B {
    ...
    @Id
    @OneToOne(targetEntity = A.class)
    @JoinColumn(name="A_ID")
    public A getA();
    ...
}

此设置也存在同样的问题。例外是:

Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("SOME_USER"."B"."A_ID")

Error Code: 1400
Call: INSERT INTO B (SOME_FIELD1, SOME_FIELD2, ..., A_ID) VALUES (?, ?, ..., ?)
bind => [null, SOME_VALUE, ..., null]
Query: InsertObjectQuery(packagename.B@ObjectId)

绑定行上的最后一个null需要由EclipseLink填充它在插入对象A时先前从序列中检索到的实际ID。 我正在做的合并归结为基本上这个:

public void merge(A objectA, B objectB) {
    objectA.setB(objectB);
    entitymanager.merge(objectA);
}

3 个答案:

答案 0 :(得分:1)

以下是how to do it with Hibernate的文档。由于它是标准的JPA,您应该可以使用EclipseLink执行相同的操作。

您将B中的A_ID替换为A的映射OneToOne关联(并在另一侧使用mappedBy),或者保留A_ID,还要向A添加OneToOne关联并使用{注释它{1}}(另外你也使用@MapsId)。

答案 1 :(得分:1)

问题似乎是你没有在你的B上设置A.

你必须保持双向关系,没有任何魔法为你做到这一点。

当您创建A并分配B时,您还必须将A分配给B,否则它将为null,并将插入null。因此,您应该这样做:

public void merge(A objectA, B objectB) {
    A.setB(objectB); //so when you merge A it knows about the B objects
    B.setA(objectA); //so B knows to look at the A object for it's key
    entitymanager.merge(objectA);
}

此外,在您的第一个示例中,您在B中也有一个重复的A_ID字段,因此您在设置A的B时也必须设置此值。确保您先保留A,否则其ID将为null,您可以通过删除A_ID并将@Id放在OneToOne上来避免这种情况,就像在第二个示例中所做的那样。您也可以在A_ID中插入insert / updateable = false,在OneToOne中输入true,然后外键值将来自OneToOne。

您得到多个可写映射错误,因为A_ID和OneToOne都映射到同一列,您需要标记其中一个insert / updateable = false。

请参阅, http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_and_ManyToOne_Relationships

和, http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side

答案 2 :(得分:0)

我收到此错误是因为我一直在做em.persist(父级),即使我一直在做parent.setChildren(child)。所以我不得不添加child.setParent(parent)。但它与你坚持的实体相关,因为你也可以做em.persist(孩子)。为简洁起见,尝试在两个实体中设置关系。

额外的评论,当我按照我之前所说的那样做时,我得到了一个" ORA-02291"错误,与我的外键不一致。这是因为我有一个TRIGGER设置我的表的PK,我也在我的实体中使用@SequenceGenerator。为了解决这个问题,我必须在我的TRIGGER中验证,如果:new.PK_TABLE为null,则只能将PK设置为MYSEQUENCE.nexval。

希望这可以帮助那些遇到同样麻烦的人,因为我从昨天开始就遇到了这个问题而且太浪费时间了。