@OneToMany和复合主键?

我正在使用带有注释的Hibernate(在春季),并且我有一个对象,它具有有序的多对一关系,该子对象具有复合主键,其一个组件是返回父对象id的外键。

结构如下所示:

+=============+                 +================+
| ParentObj   |                 | ObjectChild    |
+-------------+ 1          0..* +----------------+
| id (pk)     |-----------------| parentId       |
| ...         |                 | name           |
+=============+                 | pos            |
                                | ...            |
                                +================+

我尝试了各种注释组合,但似乎都不起作用。这是我能够想到的最接近的:

@Entity
public class ParentObject {
    @Column(nullable=false, updatable=false)
    @Id @GeneratedValue(generator="...")
    private String id;

    @OneToMany(mappedBy="parent", fetch=FetchType.EAGER, cascade={CascadeType.ALL})
    @IndexColumn(name = "pos", base=0)
    private List<ObjectChild> attrs;

    ...
}

@Entity
public class ChildObject {
    @Embeddable
    public static class Pk implements Serializable {
        @Column(nullable=false, updatable=false)
        private String parentId;

        @Column(nullable=false, updatable=false)
        private String name;

        @Column(nullable=false, updatable=false)
        private int pos;

        @Override
        public String toString() {
            return new Formatter().format("%s.%s[%d]", parentId, name, pos).toString();
        }

        ...
    }

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name="parentId")
    private ParentObject parent;

    ...
}

经过长时间的实验,我得出了这一点,其中我的大多数其他尝试都产生了由于各种原因而休眠甚至无法加载的实体。

更新:感谢大家的评论;我取得了一些进展。我做了一些调整,我认为它更接近(我已经更新了上面的代码)。但是,现在问题出在插入上。父对象似乎保存得很好,但子对象没有保存,我已经能够确定的是休眠没有填充子对象的(复合)主键的parentId部分,所以我得到一个非唯一的错误:

org.hibernate.NonUniqueObjectException:
   a different object with the same identifier value was already associated 
   with the session: [org.kpruden.ObjectChild#null.attr1[0]]

我正在自己的代码中填充和属性,但当然我不知道父ID,因为它尚未保存。关于如何说服冬眠来填补这个的任何想法?namepos

谢谢!


答案 1

Manning的书Java Persistence with Hibernate在第7.2节中有一个例子概述了如何做到这一点。幸运的是,即使您不拥有这本书,您也可以通过下载 Caveat Emptor 示例项目的 JPA 版本(此处直接链接)并检查类和包中来查看此书的源代码示例。CategoryCategorizedItemauction.model

我还将总结以下关键注释。如果这仍然是一个禁区,请让我知道。

父对象:

@Entity
public class ParentObject {
   @Id @GeneratedValue
   @Column(name = "parentId", nullable=false, updatable=false)
   private Long id;

   @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
   @IndexColumn(name = "pos", base=0)
   private List<ChildObject> attrs;

   public Long getId () { return id; }
   public List<ChildObject> getAttrs () { return attrs; }
}

子对象:

@Entity
public class ChildObject {
   @Embeddable
   public static class Pk implements Serializable {
       @Column(name = "parentId", nullable=false, updatable=false)
       private Long objectId;

       @Column(nullable=false, updatable=false)
       private String name;

       @Column(nullable=false, updatable=false)
       private int pos;
       ...
   }

   @EmbeddedId
   private Pk id;

   @ManyToOne
   @JoinColumn(name="parentId", insertable = false, updatable = false)
   @org.hibernate.annotations.ForeignKey(name = "FK_CHILD_OBJECT_PARENTID")
   private ParentObject parent;

   public Pk getId () { return id; }
   public ParentObject getParent () { return parent; }
}

答案 2

您应该将引用合并到父 Id 和 parentId 中,而不是分别映射父 Id 和 parentId:ParentObjectChildObject.Pk

(省略了 getter、setters、Hibernate 与问题无关的属性和成员访问关键字)

class ChildObject { 
    @Embeddable
    static class Pk {
        @ManyToOne...
        @JoinColumn(name="parentId")
        ParentObject parent;

        @Column...
        String name...
        ...
    }

    @EmbeddedId
    Pk id;
}

在你然后只是放,它的工作原理。ParentObject@OneToMany(mappedBy="id.parent")


推荐