我正在努力获得与JPA(提供商:Hibernate)一起使用的单向一对一关系。在我看来,这不应该是一个麻烦,但显然JPA / Hibernate不同意这一点; - )
问题是我必须映射一个我无法更改的遗留模式,并且此模式在两个实体之间使用共享主键,同时它是一个实体的外键。
我创建了一个简单的TestCase:
DB如下所示:
CREATE TABLE PARENT (PARENT_ID Number primary key, Message varchar2(50));
CREATE TABLE CHILD (CHILD_ID Number primary key, Message varchar2(50),
CONSTRAINT FK_PARENT_ID FOREIGN KEY (CHILD_ID )REFERENCES PARENT (PARENT_ID));
CREATE SEQUENCE SEQ_PK_PARENT START WITH 1 INCREMENT BY 1 ORDER;
父母(=一对一的拥有方)如下所示:
@Entity
@Table(name = "PARENT")
public class Parent implements java.io.Serializable {
private Long parentId;
private String message;
private Child child;
@Id
@Column(name = "PARENT_ID", unique = true, nullable = false, precision = 22, scale = 0)
@SequenceGenerator(name="pk_sequence", sequenceName="SEQ_PK_PARENT")
@GeneratedValue(generator="pk_sequence", strategy=GenerationType.SEQUENCE)
public Long getParentId() {
return this.parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
@Column(name = "MESSAGE", length = 50)
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
@OneToOne (cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn(name="PARENT_ID", referencedColumnName="CHILD_ID")
public Child getTestOneToOneChild() {
return this.child;
}
public void setTestOneToOneChild(Child child) {
this.child = child;
}
}
孩子:
@Entity
@Table(name = "TEST_ONE_TO_ONE_CHILD", schema = "EXTUSER")
public class Child implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Long childId;
private String message;
public Child() {
}
public Child(String message) {
this.message = message;
}
@Id
@Column(name = "CHILD_ID")
public Long getChildId() {
return this.childId;
}
public void setChildId(Long childId) {
this.childId = childId;
}
@Column(name = "MESSAGE", length = 50)
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
我完全看到JPA不知道如何为孩子分配id的问题。然而,我也试过使用Hibernates“外来”密钥生成器也没有成功,因为那个人需要有一个来自孩子的父亲的后向引用,这是不可取的。 这个问题对我来说似乎并不常见,所以我在这里缺少什么?有解决方案吗?如果纯JPA没有提供解决方案,我也可以使用hibernate扩展。
我对正确行为的期望是: 如果我试图坚持附带孩子的父母:
如果我试图坚持一个“独立”子节点(例如entityManager.persist(aChild)),我会期望一个RuntimeException。
非常感谢任何帮助!
答案 0 :(得分:4)
对于您描述的db模式,您可以在依赖类(您的Child类)上使用@MapsId注释来实现映射回父级,如下所示:
@Entity
class Parent {
@Id
@Column(name = "parent_id")
@GeneratedValue
Long parent_id;
}
@Entity
class Child {
@Id
@Column(name = "child_id")
Long child_id;
@MapsId
@OneToOne
@JoinColumn(name = "child_id")
Parent parent;
}
如您所列,使用@PrimaryKeyJoinColumn注释添加从父级到子级的映射,使完整的双向一对一映射如下所示:
@Entity
class Parent {
@Id
@Column(name = "parent_id")
@GeneratedValue
Long parent_id;
@OneToOne
@PrimaryKeyJoinColumn(name="parent_id", referencedColumnName="child_id")
public Child;
}
@Entity
class Child {
@Id
@Column(name = "child_id")
Long child_id;
@MapsId
@OneToOne
@JoinColumn(name = "child_id")
Parent parent;
}
我使用了字段而不是方法访问(并删除了与关系无关的任何内容),但这将是应用于getter的相同注释。
另请参阅2.2.3.1 here部分的最后一部分,了解@MapsId的另一个例子。
答案 1 :(得分:3)
因为那个人需要有一个来自孩子的父母的后向引用,这是不可取的
好吧,如果一个孩子只有父母才能存在,那么就是他们之间的关系。您可能不想在OO中表达,但它确实存在于关系模型中。
那就是说,我会说这是一个自然的解决方案,就是在孩子身上有一个父母。
但是如果你真的不想这样做,我建议你看一下将ID映射为PK类,并使用@EmbeddedId与两个类共享它们。我很确定它会解决你的问题,但有一个例外:
如果我试图坚持一个“独立”子节点(例如entityManager.persist(aChild)),我会期望一个RuntimeException。
如果您决定在PK类中使用@EmbeddedId方法,我认为您需要将上述情况作为“业务规则”来处理。
答案 2 :(得分:0)
此问题的解决方案是在父实体上使用@PostPersist注释。 您必须在父实体类中创建一个方法并使用@PostPersist对其进行注释,因此在父实体持久化之后将调用此方法,并且在此方法中只需设置子实体类的id。请参阅下面的示例。
@Entity
@Table(name = "PARENT")
public class Parent implements java.io.Serializable {
private Long parentId;
private String message;
private Child child;
@Id
@Column(name = "PARENT_ID", unique = true, nullable = false, precision = 22, scale = 0)
@SequenceGenerator(name="pk_sequence", sequenceName="SEQ_PK_PARENT")
@GeneratedValue(generator="pk_sequence", strategy=GenerationType.SEQUENCE)
public Long getParentId() {
return this.parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
@OneToOne (cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public Child getTestOneToOneChild() {
return this.child;
}
public void setTestOneToOneChild(Child child) {
this.child = child;
}
@PostPersist
public void initializeCandidateDetailID()
{
System.out.println("reached here");// for debugging purpose
this.child.setChildId(parentId); // set child id here
System.out.println("reached here"+Id); // for debugging purpose
}
}