Java / Hibernate / JPA:无法使用复合键持久化 - >瞬态对象

时间:2013-10-18 20:41:26

标签: java hibernate jpa

我的问题是我无法保存我的实体,因为它包含另一个实体,由一个键映射,该键也是该表的主键的一部分。该表如下所示:

table C:

+-----+------+
| id_A | id_B |
+-----+------+

..其中idA是表A的主键EntityA而idB是表B的主键EntityB

所以它基本上是一个与n的关系。这是我用于表C的实体:

@Entity
public class EntityC {

    private long idA;
    private EntityB b;

    @Id
    @Column(name = "id_A")
    public long getIdA() {
        return idA;
    }

    @Id
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_B")
    public EntityB getB() {
        return b;
    }

    ...setters are here...

}

请注意,id_A按原样(id)映射,而id_B映射为其对象表示EntityB。这就是我想用它做的事情:

EntityC c = new EntityC();
c.setIdA(123);
c.setB(new EntityB());

em.persist(c);
tx.commit();
em.close();

如果我能坚持EntityB,我想坚持EntityC

<{1>} tx.commit()我收到此异常:org.hibernate.TransientObjectException: object references an unsaved transient instance

我认为这是因为部分主键id_B未保存。但我设置级联到所有,所以应该没有问题!

为什么这不起作用?


编辑:

当我这样做时:

em.persist(c.getB());
em.persist(c);

它有效。但Hibernate / JPA不能自动执行此操作吗?我认为这就是级联的好处。


EDIT2:

添加了embeddedId而不是id_A和id_B:

@Embeddable
public class EntityCID implements Serializable {

public long idA;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_B", referencedColumnName = "id")
public EntryB b;

}

EntityC现在看起来像:

@Entity
public class EntityC implements Serializable {

    private EntityCID id;
    ...

    @EmbeddedId
    public void getId() {
        return id;
    }

}

但如果我em.persist(c.getId().b);之前没有em.persist(c),我仍会得到瞬态对象异常。坚持这一点,虽然它很难看。

@Trein:它不是双向的。 EntityB代码:

@Entity
public class EntityB implements Serializable {
    public long id;
    public String text;
}

3 个答案:

答案 0 :(得分:0)

这是双向关系吗?我建议您删除@Id getB()并执行修改:

@OneToOne(cascade = CascadeType.ALL, mappedBy = "id_B")
@PrimaryKeyJoinColumn(name = "id_B")
public EntityB getB() {
    return b;
}

您的实体类必须只有一个用@Id注释的属性。通常在需要时,创建一个将存储这两个属性的类,这将作为Id类。

答案 1 :(得分:0)

您无法传递新的Entity()以供参考。因为它中没有任何值(即使是主键)。那么hibernate如何将它作为外键插入到表中。如果未保存,cascade将保存您的父对象,无需为所有人​​调用save方法。但是当你传递新物体时,它不会这样做。

答案 2 :(得分:0)

如果你仔细想想,你看到的东西是完全合理的。

EntityC是关系C&lt;&gt; B的'拥有方':它定义了JoinColumn,而EntityB具有'mappedBy'属性。

所以在保存C时,事件的顺序通常是:

  • 插入C / update C
  • 插入B / update B

现在在你的情况下,这会导致问题,因为如果B先被保留,显然只能保存C.

就你上面的陈述而言:我想坚持“EntityB ONLY,如果我能坚持EntityC。”怎么会这样呢?

JPA有一个'派生标识符'的概念,我并不过分熟悉,但在Pro JPA一书中定义的是:

  

当一个实体中的标识符包含另一个实体的外键时   实体,我们称之为派生标识符。因为实体包含   派生标识符依赖于另一个实体的身份,   我们称第一个是依赖实体。它所依赖的实体   是一个多对一或一对一的关系的目标   依赖实体,称为父实体

现在,尽管最初建议您定义了两个@Id属性并且这是错误的,但是看起来在1-2-m上额外的@Id实际上在JPA 2中是有效的。

本书提供了许多处理派生标识符的方法,但下面给出的一个例子与你的案例非常相似。因此,您可能希望进一步调查@MapsId属性。

@Entity
public class Project {

@EmbeddedId private ProjectId id;
@MapsId("dept")
@ManyToOne
@JoinColumns({
@JoinColumn(name="DEPT_NUM", referencedColumnName="NUM"),
@JoinColumn(name="DEPT_CTRY", referencedColumnName="CTRY")})
private Department department;
// ...
}

@Embeddable
public class ProjectId implements Serializable {

@Column(name="P_NAME")
private String name;
@Embedded
private DeptId dept;
// ...
}

进一步了解:

How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6