@OneToMany 和复合主键?

发布于 2024-08-28 12:11:19 字数 2122 浏览 10 评论 0 原文

我正在使用 Hibernate 和注释(在 spring 中),并且我有一个对象,它具有有序的多对一关系,其中一个子对象具有复合主键,其中一个组件是返回到的外键父对象的 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;

    ...
}

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

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

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

我正在自己的代码中填充 namepos 属性,但我当然不知道父 ID,因为它尚未保存。关于如何说服 hibernate 填写此内容有什么想法吗?

谢谢!

I'm using Hibernate with annotations (in spring), and I have an object which has an ordered, many-to-one relationship which a child object which has a composite primary key, one component of which is a foreign key back to the id of the parent object.

The structure looks something like this:

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

I've tried a variety of combinations of annotations, none of which seem to work. This is the closest I've been able to come up with:

@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;

    ...
}

I arrived at this after a long bout of experimentation in which most of my other attempts yielded entities which hibernate couldn't even load for various reasons.

UPDATE: Thanks all for the comments; I have made some progress. I've made a few tweaks and I think it's closer (I've updated the code above). Now, however, the issue is on insert. The parent object seems to save fine, but the child objects are not saving, and what I've been able to determine is that hibernate is not filling out the parentId part of the (composite) primary key of the child objects, so I'm getting a not-unique error:

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

I'm populating the name and pos attributes in my own code, but of course I don't know the parent ID, because it hasn't been saved yet. Any ideas on how to convince hibernate to fill this out?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

顾忌 2024-09-04 12:11:20

经过多次尝试和挫折后,我最终确定我不能完全做我想做的事。

最终,我继续为子对象提供了自己的合成密钥,并让 Hibernate 管理它。这并不理想,因为密钥几乎与其余数据一样大,但它有效。

After much experimentation and frustration, I eventually determined that I cannot do exactly what I want.

Ultimately, I went ahead and gave the child object its own synthetic key and let Hibernate manage it. It's a not ideal, since the key is almost as big as the rest of the data, but it works.

ぺ禁宫浮华殁 2024-09-04 12:11:20

发现这个问题正在寻找其问题的答案,但它的答案并没有解决我的问题,因为我正在寻找 @OneToMany ,它不太适合我的表结构追赶。 @ElementCollection 非常适合我的情况。但我认为它的一个陷阱是,它将整行关系视为唯一,而不仅仅是行 id。

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

@ElementCollection
@CollectionTable( name = "chidren", joinColumns = @JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;

...
}

@Embeddable
public static class ObjectChild 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();
    }

    ... getters and setters REQUIRED (at least they were for me)
}

Found this question searching for the answer to it's problem, but it's answers didn't solve my problem, because I was looking for @OneToMany which isn't as good of a fit for the table structure I was going after. @ElementCollection is the right fit in my case. One of the gotchas of it I believe though is that it looks at the entire row of relations as being unique, not just the rows id.

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

@ElementCollection
@CollectionTable( name = "chidren", joinColumns = @JoinColumn( name = "parent_id" ) )
private List<ObjectChild> attrs;

...
}

@Embeddable
public static class ObjectChild 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();
    }

    ... getters and setters REQUIRED (at least they were for me)
}
街角卖回忆 2024-09-04 12:11:20

看起来你已经非常接近了,我正在尝试在当前的系统中做同样的事情。我从代理键开始,但想删除它,转而使用由父项的 PK 和列表中的索引组成的复合主键。

我能够通过使用“外部”生成器获得从主表共享 PK 的一对一关系:

@Entity
@GenericGenerator(
    name = "Parent",
    strategy = "foreign",
    parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
    @Id
    @GeneratedValue(generator = "Parent")
    @Column(name = "parent_id")
    private int parentId;

    @OneToOne(mappedBy = "childObject")
    private ParentObject parentObject;
    ...
}

我想知道您是否可以添加 @GenericGenerator 和 @GenerateValue 来解决 Hibernate 不分配父级的问题插入期间新获得的 PK。

It seems that you got pretty close, and I am trying to do the same thing in my current system. I started with the surrogate key but would like to remove it in favor of a composite primary key consisting of the parent's PK and the index in the list.

I was able to get a one-to-one relationship that shares the PK from the master table by using a "foreign" generator:

@Entity
@GenericGenerator(
    name = "Parent",
    strategy = "foreign",
    parameters = { @Parameter(name = "property", value = "parent") }
)
public class ChildObject implements Serializable {
    @Id
    @GeneratedValue(generator = "Parent")
    @Column(name = "parent_id")
    private int parentId;

    @OneToOne(mappedBy = "childObject")
    private ParentObject parentObject;
    ...
}

I wonder if you could add the @GenericGenerator and @GeneratedValue to solve the problem of Hibernate not assigning the parent's newly acquired PK during insertion.

把回忆走一遍 2024-09-04 12:11:20

保存父对象后,您必须在子对象中显式设置parentId,以便子对象上的插入起作用。

After saving the Parent object, you have to explicitly set the parentId in the Child objects for the inserts on the Child objects to work.

恏ㄋ傷疤忘ㄋ疼 2024-09-04 12:11:20

经过三天的研究,我想我已经找到了解决方案,但说实话,我不喜欢它,而且它绝对可以改进。然而,它确实有效并解决了我们的问题。

这是您的实体构造函数,但您也可以在 setter 方法中执行此操作。
另外,我使用了 Collection 对象,但它应该与 List 相同或相似:

...
public ParentObject(Collection<ObjectChild> children) {
    Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
    for(ObjectChild obj:children){
        obj.setParent(this);
        occ.add(obj);
    }
    this.attrs = occ;
}
...

基本上,正如其他人建议的那样,我们必须首先手动设置所有子级的父级 id,然后再保存父级(以及所有子级)

After spending three days on this, I think I have found a solution, but to be honest, I don't like it and it can definitely be improved. However, it works and solves our problem.

Here is your entity constructor, but you could also do it in the setter method.
Also, I used a Collection object but it should be same or similar with List:

...
public ParentObject(Collection<ObjectChild> children) {
    Collection<ObjectChild> occ = new ArrayList<ObjectChild>();
    for(ObjectChild obj:children){
        obj.setParent(this);
        occ.add(obj);
    }
    this.attrs = occ;
}
...

Basically, as someone else suggested, we must first manually set all the children's parent id before saving the parent (along with all children)

ぃ弥猫深巷。 2024-09-04 12:11:20

我一直在努力寻找答案,但找不到可行的解决方案。虽然我在父项中正确地使用了 OneToMany,在子项中正确地设置了 ManyToOne,但在父项保存期间,子项的密钥没有被分配,而是从父项自动生成的值。

我的问题通过在子实体(Java 类)中的 @ManyToOne 映射上方添加注释 javax.persistence.MapsId 得到解决,

@MapsId("java_field_name_of_child's_composite_key_that_needs_the_value_from_parent")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID", nullable = false, insertable = false, updatable = false)
private Parent parent;

这是 @Pascal Thivent 回答的内容(于 2010 年 4 月 10 日 1:40 回答)

请参阅本线程前面他的帖子中的示例代码片段。

谢谢,
PJR。

I was badly looking for an answer but couldn't find a working solution. Though I had the OneToMany in parent correctly and ManyToOne in child correctly, during parent's save, child's key was not getting assigned, the auto-generated value from parent.

My problem was fixed upon adding an annotation javax.persistence.MapsId above the @ManyToOne mapping in the child entity (Java class)

@MapsId("java_field_name_of_child's_composite_key_that_needs_the_value_from_parent")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID", nullable = false, insertable = false, updatable = false)
private Parent parent;

This is on top of what was answered by @Pascal Thivent (answered on Apr 10 '10 at 1:40)

Please refer to the example code snippet in his post, earlier in this thread.

Thanks,
PJR.

做个少女永远怀春 2024-09-04 12:11:19

Manning 的书 Java Persistence with Hibernate 在第 7.2 节中提供了一个示例,概述了如何执行此操作。幸运的是,即使您没有这本书,您也可以通过下载 买者自负示例项目(直接链接此处< /a>)并检查 auction.model 包中的 CategoryCategorizedItem 类。

我还将总结下面的关键注释。如果仍然不行,请告诉我。

父对象:

@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; }
}

The Manning book Java Persistence with Hibernate has an example outlining how to do this in Section 7.2. Fortunately, even if you don't own the book, you can see a source code example of this by downloading the JPA version of the Caveat Emptor sample project (direct link here) and examining the classes Category and CategorizedItem in the auction.model package.

I'll also summarize the key annotations below. Do let me know if it's still a no-go.

ParentObject:

@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; }
}

ChildObject:

@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; }
}
静谧幽蓝 2024-09-04 12:11:19

您应该将 ParentObject 引用合并到 ChildObject.Pk 中,而不是分别映射parent 和parentId:(

与问题无关的getter、setter、Hibernate 属性以及省略的成员访问关键字)

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

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

    @EmbeddedId
    Pk id;
}

ParentObject 中,您只需输入 @OneToMany(mappedBy="id.parent") 即可。

You should incorporate the ParentObject reference just into ChildObject.Pk rather than map parent and parentId separately:

(getters, setters, Hibernate attributes not related to problem and member access keywords omitted)

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

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

    @EmbeddedId
    Pk id;
}

In ParentObject you then just put @OneToMany(mappedBy="id.parent") and it works.

轻拂→两袖风尘 2024-09-04 12:11:19

首先,在ParentObject中,“修复”应设置为“parent”mappedBy属性。另外(但这可能是一个拼写错误)添加一个 @Id 注释:

@Entity
public class ParentObject {
    @Id
    @GeneratedValue
    private String id;

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

    // getters/setters
}

然后,在 ObjectChild 中,向 添加一个 name 属性组合键中的 objectId

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

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

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

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private ParentObject parent;

    // getters/setters

}

AND 还将 insertable = false, updatable = false 添加到 @JoinColumn 因为我们正在重复该实体映射中的 parentId 列。

通过这些更改,坚持并读取实体对我来说效果很好(使用 Derby 进行了测试)。

Firstly, in the ParentObject, "fix" the mappedBy attribute that should be set to "parent". Also (but this is maybe a typo) add an @Id annotation:

@Entity
public class ParentObject {
    @Id
    @GeneratedValue
    private String id;

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

    // getters/setters
}

Then, in ObjectChild, add a name attribute to the objectId in the composite key:

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

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

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

    @EmbeddedId
    private Pk pk;

    @ManyToOne
    @JoinColumn(name = "parentId", insertable = false, updatable = false)
    private ParentObject parent;

    // getters/setters

}

AND also add insertable = false, updatable = false to the @JoinColumn because we are repeating the parentId column in the mapping of this entity.

With these changes, persisting and reading the entities is working fine for me (tested with Derby).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文