访存优化

在多个级别上检索根实体及其子关联是很常见的。

在我们的示例中,我们需要使用其树,分支和叶子加载森林,并且我们将尝试查看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=?

因此,我的解决方案是简单地获取最低级别的子级,并在实体层次结构中一直获取所有需要的关联。

运行此代码:

List<BagLeaf> leaves = transactionTemplate.execute(new TransactionCallback<List<BagLeaf>>() {@Overridepublic List<BagLeaf> doInTransaction(TransactionStatus transactionStatus) {List<BagLeaf> 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);
ClassId<BagForest> forestClassId = new ClassId<BagForest>(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) {Class<T> clazz = (Class<T>) object.getClass();EntityVisitor<T, 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) {Class<T> clazz = (Class<T>) object.getClass();ClassId<T> objectClassId = new ClassId<T>(clazz, object.getId());boolean objectVisited = entityContext.isVisited(objectClassId);if (!objectVisited) {entityContext.visit(objectClassId, object);}P parent = getParent(object);if (parent != null) {Class<P> parentClass = (Class<P>) parent.getClass();ClassId<P> parentClassId = new ClassId<P>(parentClass, parent.getId());if (!entityContext.isVisited(parentClassId)) {setChildren(parent);}List<T> children = getChildren(parent);if (!objectVisited) {children.add(object);}}
}

此代码打包为实用程序,并且通过扩展EntityVisitors来进行自定义,如下所示:

public static EntityVisitor<BagForest, Identifiable> ENTITY_VISITOR = new EntityVisitor<BagForest, Identifiable>(BagForest.class) {};public static EntityVisitor<BagTree, BagForest> ENTITY_VISITOR = new EntityVisitor<BagTree, BagForest>(BagTree.class) {public BagForest getParent(BagTree visitingObject) {return visitingObject.getForest();}public List<BagTree> getChildren(BagForest parent) {return parent.getTrees();}public void setChildren(BagForest parent) {parent.setTrees(new ArrayList<BagTree>());}
};public static EntityVisitor<BagBranch, BagTree> ENTITY_VISITOR = new EntityVisitor<BagBranch, BagTree>(BagBranch.class) {public BagTree getParent(BagBranch visitingObject) {return visitingObject.getTree();}public List<BagBranch> getChildren(BagTree parent) {return parent.getBranches();}public void setChildren(BagTree parent) {parent.setBranches(new ArrayList<BagBranch>());}
};public static EntityVisitor<BagLeaf, BagBranch> ENTITY_VISITOR = new EntityVisitor<BagLeaf, BagBranch>(BagLeaf.class) {public BagBranch getParent(BagLeaf visitingObject) {return visitingObject.getBranch();}public List<BagLeaf> getChildren(BagBranch parent) {return parent.getLeaves();}public void setChildren(BagBranch parent) {parent.setLeaves(new ArrayList<BagLeaf>());}
};

这不是“本身”的访客模式,但与它有点类似。 尽管只使用索引列表或集合总会更好,但是您仍然可以使用单个查询Bags来获得关联图。

  • 代码可在GitHub上获得 。
参考: Hibernate Fact:从我们的JCG合作伙伴 Vlad Mihalcea的Vlad Mihalcea博客博客中进行多级获取 。

翻译自: https://www.javacodegeeks.com/2013/11/hibernate-facts-multi-level-fetching.html

访存优化

访存优化_Hibernate事实:多级访存相关推荐

  1. 非一致性访存系统_Hibernate事实:访存策略的重要性

    非一致性访存系统 在使用ORM工具时,每个人都承认数据库设计和实体到表映射的重要性. 这些方面引起了很多关注,而诸如获取策略之类的事情可能只是推迟了. 我认为,不应将实体获取策略与实体映射设计分开,因 ...

  2. 【显存优化】深度学习显存优化方法

    深度学习gpu的显存至关重要,显存过小的话,模型根本无法跑起来,本文介绍几种显存不足时的优化方法,能够降低深度学习模型的显存要求. 目录 一.梯度累加 二.混合精度 1.权重备份 2.损失缩放 3.精 ...

  3. 深度学习分布式策略优化、显存优化、通信优化、编译优化综述

    综述 因为我个人最近在从事可能是AI领域对性能挑战最大的方向,自动驾驶领域,所以对整个深度学习训练的优化尤为关注,最近一直在学习相关内容,谨以此篇文章做一个总结. 我一直很看好深度学习训练优化这个方向 ...

  4. MegEngine亚线性显存优化

    MegEngine亚线性显存优化 MegEngine经过工程扩展和优化,发展出一套行之有效的加强版亚线性显存优化技术,既可在计算存储资源受限的条件下,轻松训练更深的模型,又可使用更大batch siz ...

  5. 深度解析MegEngine亚线性显存优化技术

    基于梯度检查点的亚线性显存优化方法[1]由于较高的计算/显存性价比受到关注.MegEngine经过工程扩展和优化,发展出一套行之有效的加强版亚线性显存优化技术,既可在计算存储资源受限的条件下,轻松训练 ...

  6. tensorflow 显存 训练_【他山之石】训练时显存优化技术——OP合并与gradient checkpoint...

    作者:bindog 地址:http://bindog.github.io/ 01 背景 前几天看到知乎上的文章FLOPs与模型推理速度[1],文中提到一个比较耗时又占显存的pointwise操作x * ...

  7. 深度学习 占用gpu内存 使用率为0_深度解析MegEngine亚线性显存优化技术

    作者 | 旷视研究院 编辑 | Linda 基于梯度检查点的亚线性显存优化方法 [1] 由于较高的计算 / 显存性价比受到关注.MegEngine 经过工程扩展和优化,发展出一套行之有效的加强版亚线性 ...

  8. 显存优化 | Pytorch的显存机制torch.cuda.empty_cache及周边概念

    注:文中涉及一些内部底层实现机制,可能和大家外界看到的不一样,这里略过不做介绍.借着笔记,分享平时碰到的技术点,不高端,不炫酷,对你有用更好了. 最近在做模型的优化工作,主要涉及精度,速度和显存优化, ...

  9. 显存优化:纹理压缩功能介绍与使用说明

    由于近期在开发者群里发现一些开发者对纹理压缩不太理解,遇到一些使用上的问题,所以本次文章中对纹理压缩进行详细的说明和使用上的介绍,希望能对有需求的开发者带来帮助. 本篇文章已经先更新到官网的文档中,也 ...

最新文章

  1. python中in的底层实现_python中print和input的底层实现
  2. selenium3浏览器驱动安装设置方法
  3. java String format占位符
  4. django查询集-17
  5. Pixhawk的传感器数据(陀螺、加计等)流程
  6. 计算机系统计算机,计算机系统与计算机化系统的区别
  7. 系统之家win11最新旗舰版64位镜像v2021.07
  8. Android 使用数据库 SQlite
  9. gef 图形 如何禁止修改大小
  10. ES6新特性_ES6箭头函数的实践以及应用场景---JavaScript_ECMAScript_ES6-ES11新特性工作笔记010
  11. mysql中CONCAT值为空的问题解决办法
  12. 旧的起点(开园说明)
  13. OpenCV---模板匹配
  14. Kepware IOT Gateway Rest server
  15. sis最新地址获取方法_SIS系统在制药行业内的运用—奥格经典案例
  16. redis设置连接密码
  17. Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans,....
  18. 数据结构与算法(Python版)十六:有序表抽象数据类型及Python实现
  19. 湖仓一体(Lakehouse)是什么?
  20. jieba:一款为中文分词而生的Python库

热门文章

  1. [集训队作业2018] 万圣节的积木(李超线段树)
  2. SpringCloud Consul自定义服务注册
  3. 面试官让我讲下线程的WAITING状态,我笑了
  4. 为什么不应该重写service方法
  5. JavaFX官方教程(八)之JavaFX中的动画和视觉效果
  6. Oracle入门(十四E)之条件表达式case和deocde函数
  7. layui结合ajax实现下拉联动效果
  8. 《下辈子还教书》经典语录(1)
  9. 使用ueditor实现多图片上传案例——DaoImpl层(BaseDaoUtilImpl)
  10. / vs /*