[英]JPA / Hibernate unidirectional one-to-one mapping with shared primary key
I'm having a very hard time trying to get a unidirectional one-to-one relationship to work with JPA (Provider: Hibernate). 我正在努力获得与JPA(Provider:Hibernate)一起使用的单向一对一关系。 In my opinion this should not be too much of a hassle but apparently JPA / Hibernate disagrees on that ;-)
在我看来,这不应该是一个麻烦,但显然JPA / Hibernate不同意这一点;-)
The problem is that I have to map a legacy schema which I cannot change and that this schema uses a shared primary key between two entities which at the same time is the foreign key for one entity. 问题是我必须映射一个我无法更改的遗留模式,并且此模式在两个实体之间使用共享主键,同时它是一个实体的外键。
I created a simple TestCase: 我创建了一个简单的TestCase:
DB looks as follows: 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;
The parent(=owning side of one-to-one) looks as follows: 父母(=一对一的拥有方)看起来如下:
@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;
}
}
The 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;
}
}
I totally see the problem that JPA does not know how to assign the id for the child. 我完全看到JPA不知道如何为孩子分配id的问题。 However I also tried using Hibernates "foreign" key Generator with also no success because that one needs to have a back reference to the parent from child which is not desirable.
然而,我也试过使用Hibernates“外来”密钥生成器也没有成功,因为那个人需要有一个来自孩子的父亲的后向引用,这是不可取的。 This problem does not seem too uncommon to me, so what am I missing here?
这个问题对我来说似乎并不常见,所以我在这里缺少什么? Is there a solution at all?
有解决方案吗? I can also use hibernate extensions if pure JPA does not provide a solution.
如果纯JPA不提供解决方案,我也可以使用hibernate扩展。
My expectations for a correct behavior would be: If I try to persist the parent with a child attached: 我对正确行为的期望是:如果我试图坚持父母与附加的孩子:
If I try to persist a "standalone" child (eg entityManager.persist(aChild)) I would expect a RuntimeException. 如果我试图坚持一个“独立”子(例如entityManager.persist(aChild)),我会期望一个RuntimeException。
Any help is greatly appreciated! 任何帮助是极大的赞赏!
For the db schema you described, you can use @MapsId annotation on the dependent class (your Child class) to achieve the mapping back to the parent, like so: 对于您描述的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;
}
Adding the mapping from parent to child you use the @PrimaryKeyJoinColumn annotation as you had listed, making the complete bi-directional one-to-one mapping look like this: 添加从父级到子级的映射,您可以使用列出的@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;
}
I used field rather than method access (and removed anything extraneous to the relationships), but it would be the same annotations applied to your getters. 我使用了字段而不是方法访问(并删除了与关系无关的任何内容),但它将是应用于getter的相同注释。
Also see the last bit of section 2.2.3.1 here for another example of @MapsId. 另见2.2.3.1节的最后一位在这里为@MapsId的另一个例子。
because that one needs to have a back reference to the parent from child which is not desirable
因为那个人需要有一个来自孩子的父母的后向引用,这是不可取的
Well, if a Child can only exist if there's a Parent, then there is a relationship between them. 好吧,如果如果有一个父项的子只能存在,那么它们之间的关系。 You may just not want to express in OO, but it does exist in the relational model.
您可能不想在OO中表达,但它确实存在于关系模型中。
That said, I would say that the natural solution for this is to have a Parent in the Child. 也就是说,我会说这个问题的自然解决方案是在孩子身上有一个父母。
But if you really don't want to do that, I would suggest taking a look at mapping the ID as a PK class, and share them with both classes, using an @EmbeddedId. 但是如果你真的不想这样做,我建议你看看将ID映射为PK类,并使用@EmbeddedId与两个类共享它们。 I'm pretty sure it would solve your problem, with one exception:
我很确定它可以解决您的问题,但有一个例外:
If I try to persist a "standalone" child (eg entityManager.persist(aChild)) I would expect a RuntimeException.
如果我试图坚持一个“独立”子(例如entityManager.persist(aChild)),我会期望一个RuntimeException。
If you decide to use the @EmbeddedId approach in a PK class, I think you'll need to handle the above case as a "business rule". 如果您决定在PK类中使用@EmbeddedId方法,我认为您需要将上述情况作为“业务规则”处理。
The solution of this problem is using @PostPersist annotation on the parent Entity. 此问题的解决方案是在父实体上使用@PostPersist批注。 You have to create a method in the parent entity class and annotate it with @PostPersist, so this method will be invoked after parent entity is persist, and in this method just set the id of the child entity class.
您必须在父实体类中创建一个方法并使用@PostPersist对其进行注释,因此在父实体持久化之后将调用此方法,并且在此方法中只需设置子实体类的id。 See the example below.
请参阅下面的示例。
@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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.