假设我有一个实体“Parent”,它包含一个“Child”对象列表。
在Java中,这看起来像这样:
public class ParentEntity implements Parent {
protected int id;
@Override
public int getId() { return id; }
@Override
public void setId(int id) { this.id = id; }
protected List<Child> children;
@Override
public List<Child> getChildren() { return children; }
@Override
public void setChildren(List<Child> children) { this.children = children; }
@Override
public void save() {
// Do some Hibernate "save" magic here...
}
public static Parent getById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
session.close();
return entity;
}
}
我的业务逻辑类只能使用接口类,如下所示:
public class BusinessLogic {
public void doSomething() {
Parent parent = ParentEntity.getById(1);
for (Child c : parent.getChildren())
System.out.println("I love my daddy.");
}
}
不幸的是,这不起作用,因为父节点的子节点没有被加载,并且循环因NullPointerException而崩溃。
这种方法存在两个问题。即使在XML中我写了“lazy ='false'”,Hibernate似乎也忽略了这一点。 其次,在我的情况下,急切的加载是不可取的,因为我们可能有数百个孩子。
@Override
public List<Child> getChildren()
{
if (!Hibernate.isInitialized(children)) {
Session session = HibernateUtil.getSessionFactory().openSession();
Hibernate.initialize(children);
session.close();
}
return children;
}
这不起作用,因为我得到一个例外,说集合没有链接到会话。用于加载父实体的会话先前已关闭。
您的建议是“最佳实践”解决方案?我真的不想在我的业务逻辑中搞乱Hibernate会话。
答案 0 :(得分:0)
您可以使用查询对象或自定义查询,并在您真正需要它们的场景中搜索所有子项(搜索left join fetch
),对于可能有效的几千个对象。
但是,如果记录的数量达到数百万,那么你很可能会遇到内存问题,那么你应该考虑共享缓存,逐页检索数据只需搜索n + 1和hibernate,你会发现关于这个主题的大量讨论。
我能想到的最简单的 hack :
public static Parent getParentWithChildrenById(int id) {
Session session = HibernateUtil.getSessionFactory().openSession();
Parent entity = (Parent) session.get(ParentEntity.class, id);
Hibernate.initialize(entity.children);
session.close();
return entity;
}
旁注:在您的域层中拥有数据访问逻辑被认为是不好的做法。
答案 1 :(得分:0)
我总是允许Spring和JPA管理我的Hibernate会话,因此大多数痛苦的样板代码在那时消失了。但是,在退出打开会话的数据层调用之前,您仍然需要调用entity.getChildren().size()
(或类似的东西),以强制Hibernate在仍有会话要使用时进行检索。