14

我很难尝试建立单向的一对一关系来使用 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 不提供解决方案,我也可以使用休眠扩展。

我对正确行为的期望是:如果我试图坚持带孩子的父母:

  1. 从序列中获取 ID,将其设置在父级上
  2. 坚持父母
  3. 为孩子设置父母的ID
  4. 坚持孩子

如果我尝试持久化一个“独立”子级(例如 entityManager.persist(aChild)),我会期待一个 RuntimeException。

任何帮助是极大的赞赏!

4

3 回答 3

5

对于您描述的数据库模式,您可以在依赖类(您的 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 节的最后一点以获取 @MapsId 的另一个示例。

于 2012-09-07T00:27:12.367 回答
3

因为那个人需要从孩子那里反向引用父母,这是不可取的

好吧,如果 Child 只能在有 Parent 的情况下存在,那么它们之间存在关系。您可能只是不想在 OO 中表达,但它确实存在于关系模型中。

也就是说,我会说这个问题的自然解决方案是在孩子中有一个父母。

但是,如果您真的不想这样做,我建议您将 ID 映射为 PK 类,并使用 @EmbeddedId 与两个类共享它们。我很确定它会解决您的问题,但有一个例外:

如果我尝试持久化一个“独立”子级(例如 entityManager.persist(aChild)),我会期待一个 RuntimeException。

如果您决定在 PK 类中使用 @EmbeddedId 方法,我认为您需要将上述情况作为“业务规则”处理。

于 2011-01-21T13:45:37.427 回答
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
    }
}
于 2017-08-02T08:51:10.333 回答