java JPA/Hibernate 父子关系

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5340769/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:42:31  来源:igfitidea点击:

JPA/Hibernate Parent/Child relationship

javahibernateormjpa

提问by NubieJ

I am quite new to JPA/Hibernate (Java in general) so my question is as follows (note, I have searched far and wide and have not come across an answer to this):

我对 JPA/Hibernate(一般是 Java)很陌生,所以我的问题如下(注意,我已经搜索了很远很远,但没有找到答案):

I have two entities:

我有两个实体:

Parent and Child (naming changed).

父子节点(命名已更改)。

Parent contains a list of Children and Children refers back to parent.

Parent 包含一个 Children 列表,Children 引用回父级。

e.g.

例如

@Entity
public class Parent {

@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "PARENT_ID", insertable = false, updatable = false)
private int id;        

    /* ..... */

    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
    @JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID", nullable = true)
    private Set<child> children;

    /* ..... */

}

@Entity
public class Child {

@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "CHILD_ID", insertable = false, updatable = false)
private int id; 

    /* ..... */

    @ManyToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
private Parent parent;

    /* ..... */

}

I want to be able to do the following:

我希望能够做到以下几点:

  1. Retrieve a Parent entity which would contain a list of all its children (List), however, when listing Parent (getting List, it of course should omit the children from the results, therefore setting FetchType.LAZY.

  2. Retrieve a Child entity which would contain an instance of the Parent entity.

  1. 检索将包含其所有子项(列表)的列表的父实体,但是,在列出父实体(获取列表时,它当然应该从结果中省略子项,因此设置 FetchType.LAZY。

  2. 检索包含父实体实例的子实体。

Using the code above (or similar) results in two exceptions:

使用上面(或类似的)代码会导致两个异常:

  1. Retrieving Parent: A cycle is detected in the object graph. This will cause infinitely deep XML...

  2. Retrieving Child: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: xxxxxxxxxxx, no session or session was closed

  1. Retrieving Parent:在对象图中检测到一个循环。这将导致无限深的 XML...

  2. 检索子项:org.hibernate.LazyInitializationException:未能延迟初始化角色集合:xxxxxxxxxxx,没有会话或会话被关闭

When retrieving the Parent entity, I am using a named query (i.e. calling it specifically) @NamedQuery(name = "Parent.findByParentId", query = "SELECT p FROM Parent AS p LEFT JOIN FETCH p.children where p.id = :id")

检索父实体时,我使用命名查询(即专门调用它)@NamedQuery(name = "Parent.findByParentId", query = "SELECT p FROM Parent AS p LEFT JOIN FETCH p.children where p.id = : ID”)

Code to get Parent (i.e. service layer):

获取Parent(即服务层)的代码:

public Parent findByParentId(int parentId) {
    Query query = em.createNamedQuery("Parent.findByParentId");
    query.setParameter("id", parentId);

    return (Parent) query.getSingleResult();
}

Why am I getting a LazyInitializationException event though the List property on the Parent entity is set as Lazy (when retrieving the Child entity)?

尽管父实体上的 List 属性设置为 Lazy(检索子实体时),为什么我会收到 LazyInitializationException 事件?

回答by Ralph

ADDED

添加

Your mapping is a bit strange. What your mapping describe are two different one directional Releation ships:

你的映射有点奇怪。您的映射描述的是两种不同的单向关系船

  • Parent -- 1:n -> Child
  • Child -- n:1 -> Parent
  • 父级 -- 1:n -> 子级
  • 子级 -- n:1 -> 父级

which both are independend, but are stored in the same Database column.

两者都是独立的,但存储在同一个数据库列中。

I guess that this is not want you want, I guess that you want to have one bidirectional relation ship. The easyest way is to change the Parent's child Set mapping, to use mappedByinstead of @JoinColumn:

我猜这不是你想要的,我猜你想要有一个双向关系。最简单的方法是更改​​ Parent 的子 Set 映射,使用mappedBy而不是@JoinColumn

@Entity
public class Parent {
    ...
    /* fetch = FetchType.LAZY is default in one:many */
    @OneToMany(cascade = { CascadeType.ALL }, mappedBy="parent")
    private Set<child> children;    
    ...   
}

From what you described in the comments of my old answer, this should fix the problem. Because if you now naviagate Parant -> Child -> Parent, both parent will be the same, and the persistence provider know it.

从您在我的旧答案的评论中描述的内容来看,这应该可以解决问题。因为如果你现在导航 Parant -> Child -> Parent,两个 parent 将是相同的,并且持久性提供者知道这一点。

Beware of the fact, that the relationship now is only maintained on the child side.

请注意,现在的关系仅在孩子一方维持。

OLD

老的

When you get a LazyInitializationExceptionthen the problem is that the not loaded entity is not longer attached to the EntityManager. In most cases the problem is that the transaction is closed, before the entity is loaded.

当你得到一个LazyInitializationException然后问题是未加载的实体不再附加到 EntityManager。在大多数情况下,问题是在加载实体之前关闭事务。

You can do fore things:

你可以做以下事情:

  • keep the transaction open
  • load the entity before you close the transaction
  • switch from lazy to eager load
  • fix your mapping (see: the added part of my answer)
  • 保持交易开放
  • 在关闭事务之前加载实体
  • 从懒加载切换到急切加载
  • 修复您的映射(请参阅:我的答案的添加部分)

回答by ThomasRS

Exception 1. Your XML serialization does not have anything with JPA/Hibernate, you will be able to serialize your two-way-setup successfully. If automatic serialization fails, preprocess your to-be serialized objects in such a way that you remove the link from child to parent - outside the transaction (assuming you have automatic merges in your container).

例外 1. 您的 XML 序列化与 JPA/Hibernate 没有任何关系,您将能够成功地序列化您的双向设置。如果自动序列化失败,请以这样的方式预处理您要序列化的对象,即删除从子级到父级的链接 - 在事务之外(假设您的容器中有自动合并)。

Exception 2. You should read up on Transactions. In short, there are rules to when traversal of objects requiring database interaction can be done and not. Logically this divide must exist. If you want lazy relationships to behave as 'normal' (read: fetched) outside the transaction session, you simple 'prefetch' the relationship by traversing or accessing it in such a way that the relationship is resolved. For example call .size, or iterating over the members, on a set or a list will do this.

例外 2. 您应该阅读交易。简而言之,对于需要数据库交互的对象的遍历什么时候可以完成,什么时候不能完成,有一些规则。从逻辑上讲,这种鸿沟必须存在。如果您希望惰性关系在事务会话之外表现得像“正常”(读取:已获取)一样,您可以通过以解决关系的方式遍历或访问它来简单地“预取”关系。例如,在集合或列表上调用 .size 或迭代成员将执行此操作。

回答by MariemJab

I had the same problem. In fact I have a unidirectional relation (exp: Department has many Employee). So Employee will have a foreign key on Department. Once generated, Hibernate do a bidirectional relation with ManyToOne and OneToMany. So check weather you relation is uni or Bi-directional. In the first case, you need to remove the mapping oneToMany (from Department)

我有同样的问题。事实上我有一个单向关系(exp:部门有很多员工)。所以 Employee 将在 Department 上有一个外键。一旦生成,Hibernate 就会与 ManyToOne 和 OneToMany 建立双向关系。因此,请检查您的关系是单向还是双向的天气。在第一种情况下,您需要删除映射oneToMany(来自Department)

Hope that it will help.

希望它会有所帮助。