Java @OneToMany 和复合主键?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2611619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
@OneToMany and composite primary keys?
提问by Kris Pruden
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.
我正在使用带有注释的 Hibernate(在 spring 中),并且我有一个对象,它具有有序的多对一关系,其中一个子对象具有复合主键,其中一个组件是返回到父对象的 id。
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:
更新:感谢大家的评论;我取得了一些进展。我做了一些调整,我认为它更接近(我已经更新了上面的代码)。然而,现在问题出在插入上。父对象似乎保存得很好,但子对象没有保存,我已经能够确定的是休眠没有填写子对象的(复合)主键的 parentId 部分,所以我我得到一个不唯一的错误:
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?
我在自己的代码中填充name
和pos
属性,但当然我不知道父 ID,因为它还没有保存。关于如何说服 hibernate 填写此内容的任何想法?
Thanks!
谢谢!
采纳答案by Kris Pruden
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.
最终,我继续为子对象提供了自己的合成密钥,并让 Hibernate 管理它。这并不理想,因为密钥几乎与其余数据一样大,但它有效。
回答by RTBarnard
The Manning book Java Persistence with Hibernatehas 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 Emptorsample project (direct link here) and examining the classes Category
and CategorizedItem
in the auction.model
package.
Manning 书籍Java Persistence with Hibernate有一个示例,概述了如何在第 7.2 节中执行此操作。幸运的是,即使你没有自己的书,你可以通过下载的JPA版本中看到的这个源代码示例买者自负样本项目(直接链接在此),并检查类Category
和CategorizedItem
在auction.model
包中。
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; }
}
回答by Pascal Thivent
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:
首先,在ParentObject
,“修复”mappedBy
应该设置为的属性"parent"
。另外(但这可能是一个错字)添加@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
}
Then, in ObjectChild
, add a name
attribute to the objectId
in the composite key:
然后,在 中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
}
ANDalso add insertable = false, updatable = false
to the @JoinColumn
because we are repeating the parentId
column in the mapping of this entity.
AND也添加insertable = false, updatable = false
到 ,@JoinColumn
因为我们parentId
在此实体的映射中重复列。
With these changes, persisting and reading the entities is working fine for me (tested with Derby).
通过这些更改,持久化和读取实体对我来说效果很好(用 Derby 测试)。
回答by David Harkness
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.
看起来你已经很接近了,我正在尝试在我当前的系统中做同样的事情。我从代理键开始,但想删除它以支持由父项的 PK 和列表中的索引组成的复合主键。
I was able to get a one-to-one relationship that shares the PK from the master table by using a "foreign" generator:
通过使用“外部”生成器,我能够获得从主表共享 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;
...
}
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.
不知道能不能加上@GenericGenerator 和@GeneratedValue 来解决Hibernate在插入时不分配父级新获取的PK的问题。
回答by gabor
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.
保存 Parent 对象后,您必须在 Child 对象中显式设置 parentId,以便 Child 对象上的插入工作。
回答by Tomá? Zálusky
You should incorporate the ParentObject
reference just into ChildObject.Pk
rather than map parent and parentId separately:
您应该将ParentObject
引用合并到ChildObject.Pk
而不是分别映射 parent 和 parentId:
(getters, setters, Hibernate attributes not related to problem and member access keywords omitted)
(getter、setter、Hibernate 属性与问题无关,省略了成员访问关键字)
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.
在ParentObject
你然后只是把@OneToMany(mappedBy="id.parent")
它的工作原理。
回答by xenoterracide
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.
发现这个问题正在寻找它的问题的答案,但它的答案并没有解决我的问题,因为我正在寻找@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)
}
回答by Lorenzo
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:
这是您的实体构造函数,但您也可以在 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;
}
...
Basically, as someone else suggested, we must first manually set all the children's parent id before saving the parent (along with all children)
基本上,正如其他人所建议的,在保存父级(以及所有子级)之前,我们必须首先手动设置所有子级的父 ID
回答by pjr_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.
我一直在寻找答案,但找不到可行的解决方案。虽然我在父中正确地拥有 OneToMany 并且在子中正确地拥有 ManyToOne,但在父保存期间,未分配子键,即父自动生成的值。
My problem was fixed upon adding an annotation javax.persistence.MapsId above the @ManyToOne mapping in the child entity (Java class)
在子实体(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;
This is on top of what was answered by @Pascal Thivent (answered on Apr 10 '10 at 1:40)
这是在@Pascal Thivent 回答的基础上(于 2010 年 4 月 10 日 1:40 回答)
Please refer to the example code snippet in his post, earlier in this thread.
请参阅本主题前面他的帖子中的示例代码片段。
Thanks, PJR.
谢谢,PJR。