简体   繁体   English

JPA / Hibernate使用共享主键进行单向一对一映射

[英]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: 我对正确行为的期望是:如果我试图坚持父母与附加的孩子:

  1. get ID from sequence, set it on the parent 从序列中获取ID,将其设置在父级上
  2. persist parent 坚持父母
  3. set parent's ID on child 在孩子身上设置父母的ID
  4. persist child 坚持孩子

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM