我的问题是我无法保存我的实体,因为它包含另一个实体,由一个键映射,该键也是该表的主键的一部分。该表如下所示:
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
。
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;
}
答案 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<> B的'拥有方':它定义了JoinColumn,而EntityB具有'mappedBy'属性。
所以在保存C时,事件的顺序通常是:
现在在你的情况下,这会导致问题,因为如果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;
// ...
}
进一步了解: