南通住房和城乡建设局网站首页,网站建设冫首先金手指十五,亚马逊跨境电商是做什么的,wordpress页面调用子页面在多个级别上检索根实体及其子关联是很常见的。 在我们的示例中#xff0c;我们需要加载一个包含其树#xff0c;分支和叶子的森林#xff0c;并且我们将尝试查看Hibernate对于三种集合类型的行为#xff1a;集合#xff0c;索引列表和包。 这是我们的类层次结构的样子我们需要加载一个包含其树分支和叶子的森林并且我们将尝试查看Hibernate对于三种集合类型的行为集合索引列表和包。 这是我们的类层次结构的样子 使用集和索引列表很简单因为我们可以通过运行以下JPA-QL查询来加载所有实体 Forest f entityManager.createQuery(
select f
from Forest f
join fetch f.trees t
join fetch t.branches b
join fetch b.leaves l , Forest.class)
.getSingleResult(); 执行的SQL查询为 SELECT forest0_.id AS id1_7_0_,trees1_.id AS id1_18_1_,branches2_.id AS id1_4_2_,leaves3_.id AS id1_10_3_,trees1_.forest_fk AS forest_f3_18_1_,trees1_.index AS index2_18_1_,trees1_.forest_fk AS forest_f3_7_0__,trees1_.id AS id1_18_0__,trees1_.index AS index2_0__,branches2_.index AS index2_4_2_,branches2_.tree_fk AS tree_fk3_4_2_,branches2_.tree_fk AS tree_fk3_18_1__,branches2_.id AS id1_4_1__,branches2_.index AS index2_1__,leaves3_.branch_fk AS branch_f3_10_3_,leaves3_.index AS index2_10_3_,leaves3_.branch_fk AS branch_f3_4_2__,leaves3_.id AS id1_10_2__,leaves3_.index AS index2_2__
FROM forest forest0_
INNER JOIN tree trees1_ ON forest0_.id trees1_.forest_fk
INNER JOIN branch branches2_ ON trees1_.id branches2_.tree_fk
INNER JOIN leaf leaves3_ ON branches2_.id leaves3_.branch_fk 但是当我们的子级关联映射为Bags时相同的JPS-QL查询将引发“ org.hibernate.loader.MultipleBagFetchException”。 万一您不能更改映射用集合或索引列表替换包您可能会想尝试以下方法 BagForest forest entityManager.find(BagForest.class, forestId);
for (BagTree tree : forest.getTrees()) {for (BagBranch branch : tree.getBranches()) {branch.getLeaves().size(); }
} 但这会产生大量的SQL查询效率很低 select trees0_.forest_id as forest_i3_1_1_, trees0_.id as id1_3_1_, trees0_.id as id1_3_0_, trees0_.forest_id as forest_i3_3_0_, trees0_.index as index2_3_0_ from BagTree trees0_ where trees0_.forest_id?
select branches0_.tree_id as tree_id3_3_1_, branches0_.id as id1_0_1_, branches0_.id as id1_0_0_, branches0_.index as index2_0_0_, branches0_.tree_id as tree_id3_0_0_ from BagBranch branches0_ where branches0_.tree_id?
select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id?
select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id?
select branches0_.tree_id as tree_id3_3_1_, branches0_.id as id1_0_1_, branches0_.id as id1_0_0_, branches0_.index as index2_0_0_, branches0_.tree_id as tree_id3_0_0_ from BagBranch branches0_ where branches0_.tree_id?
select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id?
select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id? 因此我的解决方案是简单地获取最低级别的子级并在实体层次结构中一直获取所有需要的关联。 运行此代码 ListBagLeaf leaves transactionTemplate.execute(new TransactionCallbackListBagLeaf() {Overridepublic ListBagLeaf doInTransaction(TransactionStatus transactionStatus) {ListBagLeaf leaves entityManager.createQuery(select l from BagLeaf l inner join fetch l.branch b inner join fetch b.tree t inner join fetch t.forest f where f.id :forestId,BagLeaf.class).setParameter(forestId, forestId).getResultList();return leaves;}
}); 仅生成一个SQL查询 SELECT bagleaf0_.id AS id1_2_0_,bagbranch1_.id AS id1_0_1_,bagtree2_.id AS id1_3_2_,bagforest3_.id AS id1_1_3_,bagleaf0_.branch_id AS branch_i3_2_0_,bagleaf0_.index AS index2_2_0_,bagbranch1_.index AS index2_0_1_,bagbranch1_.tree_id AS tree_id3_0_1_,bagtree2_.forest_id AS forest_i3_3_2_,bagtree2_.index AS index2_3_2_
FROM bagleaf bagleaf0_INNER JOIN bagbranch bagbranch1_ON bagleaf0_.branch_id bagbranch1_.idINNER JOIN bagtree bagtree2_ON bagbranch1_.tree_id bagtree2_.idINNER JOIN bagforest bagforest3_ON bagtree2_.forest_id bagforest3_.id
WHERE bagforest3_.id ? 我们得到了一个叶子对象的列表但是每个叶子还获取了分支后者获取了树然后获取了森林。 不幸的是Hibernate无法从这样的查询结果中神奇地创建上下层次结构。 尝试通过以下方式进入袋子 leaves.get(0).getBranch().getTree().getForest().getTrees(); 只是抛出LazyInitializationException因为我们试图在打开的持久性上下文之外访问未初始化的惰性代理列表。 因此我们只需要从Leaf对象的List自己重新创建Forest层次结构。 这就是我的方法 EntityGraphBuilder entityGraphBuilder new EntityGraphBuilder(new EntityVisitor[] {BagLeaf.ENTITY_VISITOR, BagBranch.ENTITY_VISITOR, BagTree.ENTITY_VISITOR, BagForest.ENTITY_VISITOR
}).build(leaves);
ClassIdBagForest forestClassId new ClassIdBagForest(BagForest.class, forestId);
BagForest forest entityGraphBuilder.getEntityContext().getObject(forestClassId); EntityGraphBuilder是我编写的一个实用程序它接受EntityVisitor对象的数组并将其应用于访问的对象。 递归处理到Forest对象我们用新的Hibernate集合替换了Hibernate集合将每个子代添加到父子代集合中。 由于替换了子级集合因此不安全地在新的Persistence Context中重新附加/合并此对象是比较安全的因为所有Bags都将标记为脏。 这是实体使用其访问者的方式 private T extends Identifiable, P extends Identifiable void visit(T object) {ClassT clazz (ClassT) object.getClass();EntityVisitorT, P entityVisitor visitorsMap.get(clazz);if (entityVisitor null) {throw new IllegalArgumentException(Class clazz has no entityVisitor!);}entityVisitor.visit(object, entityContext);P parent entityVisitor.getParent(object);if (parent ! null) {visit(parent);}
} 基本的EntityVisitor看起来像这样 public void visit(T object, EntityContext entityContext) {ClassT clazz (ClassT) object.getClass();ClassIdT objectClassId new ClassIdT(clazz, object.getId());boolean objectVisited entityContext.isVisited(objectClassId);if (!objectVisited) {entityContext.visit(objectClassId, object);}P parent getParent(object);if (parent ! null) {ClassP parentClass (ClassP) parent.getClass();ClassIdP parentClassId new ClassIdP(parentClass, parent.getId());if (!entityContext.isVisited(parentClassId)) {setChildren(parent);}ListT children getChildren(parent);if (!objectVisited) {children.add(object);}}
} 此代码打包为实用程序并且通过扩展EntityVisitors来进行自定义如下所示 public static EntityVisitorBagForest, Identifiable ENTITY_VISITOR new EntityVisitorBagForest, Identifiable(BagForest.class) {};public static EntityVisitorBagTree, BagForest ENTITY_VISITOR new EntityVisitorBagTree, BagForest(BagTree.class) {public BagForest getParent(BagTree visitingObject) {return visitingObject.getForest();}public ListBagTree getChildren(BagForest parent) {return parent.getTrees();}public void setChildren(BagForest parent) {parent.setTrees(new ArrayListBagTree());}
};public static EntityVisitorBagBranch, BagTree ENTITY_VISITOR new EntityVisitorBagBranch, BagTree(BagBranch.class) {public BagTree getParent(BagBranch visitingObject) {return visitingObject.getTree();}public ListBagBranch getChildren(BagTree parent) {return parent.getBranches();}public void setChildren(BagTree parent) {parent.setBranches(new ArrayListBagBranch());}
};public static EntityVisitorBagLeaf, BagBranch ENTITY_VISITOR new EntityVisitorBagLeaf, BagBranch(BagLeaf.class) {public BagBranch getParent(BagLeaf visitingObject) {return visitingObject.getBranch();}public ListBagLeaf getChildren(BagBranch parent) {return parent.getLeaves();}public void setChildren(BagBranch parent) {parent.setLeaves(new ArrayListBagLeaf());}
}; 这不是“本身”的访客模式但与它有点类似。 尽管只使用索引列表或集合总会更好但是您仍然可以使用单个查询Bags来获得关联图。 代码可在GitHub上获得 。 参考 Hibernate Fact从我们的JCG合作伙伴 Vlad Mihalcea的Vlad Mihalcea博客博客中进行多级获取 。 翻译自: https://www.javacodegeeks.com/2013/11/hibernate-facts-multi-level-fetching.html