JPA / Hibernate父/子关系

时间:2011-03-17 14:54:28

标签: java hibernate orm jpa

我对JPA / Hibernate(一般的Java)都很陌生,所以我的问题如下(请注意,我已经进行了广泛的搜索并且没有找到答案):

我有两个实体:

父母和子女(命名已更改)。

父级包含儿童和儿童的列表,指回父母。

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;

    /* ..... */

}

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

  1. 检索一个父实体,其中包含所有子项的列表(List),但是,当列出Parent时(获取List,它当然应该省略结果中的子项,因此设置FetchType.LAZY。< / p>

  2. 检索包含Parent实体实例的Child实体。

  3. 使用上面的代码(或类似代码)导致两个例外:

    1. 检索父级: 在对象图中检测到循环。这将导致无限深的XML ......

    2. 检索孩子: org.hibernate.LazyInitializationException:懒得初始化角色集合:xxxxxxxxxxx,没有会话或会话关闭

    3. 检索Parent实体时,我使用的是命名查询(即专门调用它)     @NamedQuery(name =“Parent.findByParentId”,query =“SELECT p FROM Parent AS p LEFT JOIN FETCH p.children其中p.id =:id”)

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

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

      为什么我得到一个LazyInitializationException事件虽然Parent实体上的List属性设置为Lazy(检索Child实体时)?

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

<强> ADDED

你的映射有点奇怪。您的映射描述的是两个不同的方向Releation发货

  • 父母 - 1:n - &gt;子
  • 儿童 - n:1 - &gt;父

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

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

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

根据我在旧答案的评论中所描述的,这应该可以解决问题。因为如果你现在naviagate Parant - &gt;孩子 - &gt;父级,父级都是相同的,持久性提供者也知道它。

请注意,现在这种关系只能维持在孩子身边。

<强> OLD

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

你可以做的事情:

  • 保持交易开放
  • 在关闭交易之前加载实体
  • 从懒惰切换到急切加载
  • 修复您的映射(请参阅:我的答案中添加的部分)

答案 2 :(得分:0)

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

希望它会有所帮助。