1

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

table C:

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

..其中 idA 是表 AEntityA的主键,idB 是表 B 的主键EntityB

所以它基本上是一个n对m的关系。这是我用于表 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

tx.commit()我得到这个例外:org.hibernate.TransientObjectException: object references an unsaved transient instance

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

为什么这不起作用?


编辑:

当我这样做时:

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

有用。但是 Hibernate/JPA 不能自动做到这一点吗?我认为这就是级联的好处。


编辑2:

添加了一个 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:它不是双向的。实体B代码:

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

3 回答 3

0

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

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

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

于 2013-10-18T20:42:48.557 回答
0

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

于 2013-10-19T02:20:54.027 回答
0

如果您考虑一下,您所看到的就很有意义。

EntityC 是关系 C<>B 的“拥有方”:它定义了 JoinColumn 并且 EntityB 具有“mappedBy”属性。

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

  • 插入 C/更新 C
  • 插入 B/更新 B

现在,在您的情况下,这会导致问题,因为显然 C 只有在 B 首先被持久化的情况下才能被保存。

就您上面的陈述而言:我想坚持“仅当我可以坚持 EntityC 时才坚持 EntityB”。怎么会是这样?

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;
// ...
}

进一步看:

如何在 Hibernate 3.6 中正确级联保存主键上的一对一双向关系

于 2013-10-19T07:18:20.107 回答