休眠事实:多级访存

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前5个有用的隐藏Eclipse功能

Eclipse是野兽。 仅凭其力量才能超越其神秘感的设备。 有人将其称为连续体跨功能器 。 其他人则称它为透湿器 。 是的&#xff0c;它是如此之大&#xff0c;需要花费数年才能掌握。 然后&#xff0c;您的经理出现并告诉您&#xff1a;我们正在使用NetBeans。 开玩笑。 除了Ada…

linux如何解除密码,如何在Linux下解除PDF文件的密码?

【51CTO.com快译】今天&#xff0c;我碰巧与一位朋友共享一个受密码保护的PDF文件。我知道该PDF文件的密码&#xff0c;但不想透露。相反&#xff0c;我只想解除密码&#xff0c;将文件发送给朋友。于是我开始在网上找一些简单的方法&#xff0c;好解除PDF文件的密码保护。上网…

C#中结构体定义并转换字节数组

ref: https://www.cnblogs.com/dafanjoy/p/7818126.html C#中结构体定义并转换字节数组 最近的项目在做socket通信报文解析的时候&#xff0c;用到了结构体与字节数组的转换&#xff1b;由于客户端采用C开发&#xff0c;服务端采用C#开发&#xff0c;所以双方必须保证各自定义结…

2018移动端页面适配-自适应最新方案直接写px--------通过gulp工作流搭建一体化的移动端开发环境

1.开始 在flexible的GitHub上面写着 由于viewport单位得到众多浏览器的兼容&#xff0c;lib-flexible这个过渡方案已经可以放弃使用&#xff0c;不管是现在的版本还是以前的版本&#xff0c;都存有一定的问题。建议大家开始使用viewport来替代此方案。vw的兼容方案可以参阅《如…

jclouds的命令行界面

序幕 我使用和为jclouds贡献了一年多的时间。 到目前为止&#xff0c;我已经在很多领域广泛使用了它&#xff0c;尤其是在Fuse生态系统中 。 它的强大之处在于它缺少一件事&#xff0c;该工具可用于管理jclouds也提供访问权限的任何云提供商。 类似于EC2命令之类的工具&#xf…

中兴linux下载软件,国产操作系统中兴新支点使用WPS For Linux办公软件的体验报告...

以下将给你带来在国产操作系统中兴新支点操作系统下使用WPS For Linux办公软件的体验报告&#xff0c;WPS For Linux提供Deb、Rpm、Tar.xz、Snap软件包&#xff0c;你可以选择Tar.xz源码包编译安装&#xff0c;或在系统自带的软件中心下安装&#xff0c;也可以参考采用snap方式…

Java 教程(开发环境配置+基础语法)

Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境。 window系统安装java 下载JDK 首先我们需要下载java开发工具包JDK&#xff0c;下载地址&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/index.html&#xff0c;点击如下下载按钮&am…

数据采集工具Telegraf:简介及安装

接着上一篇博客&#xff1a;InfluxDB简介及安装&#xff0c;这篇博客介绍下Linux环境下Telegraf安装以及其功能特点。。。 官网地址&#xff1a;influxdata 官方文档&#xff1a;telegraf文档 环境&#xff1a;CentOS7.4 64位 Telegraf版本&#xff1a;0.11.1-1 一、Telegraf介…

初探小程序插件

插播公司招聘信息&#xff1a; https://cnodejs.org/topic/5a915706653c43b914684f90 小程序插件可以干嘛&#xff1f; 周二晚上&#xff08;3.13&#xff09;的一个小程序新功能发布了-【小程序插件】&#xff0c;一开始以为是小程序发布了类似npm的组件管理工具&#xff0c;…

从mysql向HBase+Phoenix迁移数据的心得总结

* 转载请注明出处 - yosql473 - 格物致知&#xff0c;经世致用 mysql -> HBase Phoenix 1.总体方案有哪些&#xff1f; 1&#xff09;通过Sqoop直接从服务器(JDBC方式)抽取数据到HBase中 因为数据量非常大&#xff0c;因此优先考虑用Sqoop和MR抽取。 使用Sqoop抽取数据有一…

玩转异步 JS :async/await 简明教程(附视频下载)

课程介绍 在软件开发领域&#xff0c;简洁的代码 > 容易阅读的代码 > 容易维护的代码&#xff0c;而 ES2017 中的 async/await 特性能让我们编写出相比回调地狱和 Promise 链式调用更直观、更容易理解的代码&#xff0c;await 关键字接收一个 Promise&#xff0c;等待代码…

分享轮子-flutter下拉刷新上拉加载

flutter下拉上拉组件轮子 什么是flutter? 首先说下flutter,估计这个应该挺多人没听过flutter这个框架,它是一个google推出的跨平台的移动应用UI框架,和React Native是同样的目的,支持三大平台:Android,Ios,还有一个是google新出的系统,忘了叫什么...本人React Native也是用过…

(8)Python判断结构

转载于:https://www.cnblogs.com/hankleo/p/9170325.html

History of program(1950-2020)

1957年 约翰巴科斯&#xff08;John Backus&#xff09;创建了是全世界第一套高阶语言&#xff1a;FORTRAN。 John Backus1959年 葛丽丝霍普&#xff08;Grace Hopper&#xff09;创造了现代第一个编译器A-0 系统&#xff0c;以及商用电脑编程语言“COBOL”&#xff0c;被誉为C…

关于 Nuxt 集成ueditor的一些坑(包括图片上传)前端部分

最近公司接了一个项目&#xff0c;里面用到富文本编辑器&#xff0c;刚开始用的是vue-quill-editor&#xff0c;这个编辑器轻量、好用。最重要的是它有专门正对nuxt的版本&#xff0c;很容易配置&#xff0c;可以放心使用&#xff0c;不用担心bug之类的&#xff0c;遇到问题&am…

*Codeforces989D. A Shade of Moonlight

数轴上$n \leq 100000$个不重叠的云&#xff0c;给坐标&#xff0c;长度都是$l$&#xff0c;有些云速度1&#xff0c;有些云速度-1&#xff0c;风速记为$w$&#xff0c;问在风速不大于$w_{max}$时&#xff0c;有几对云可能在0相遇。每一对云单独考虑。 多动一不动--相对运动。假…

undefined reference 问题各种情况分析

扒自网友文章 关于undefined reference这样的问题&#xff0c;大家其实经常会遇到&#xff0c;在此&#xff0c;我以详细地示例给出常见错误的各种原因以及解决方法&#xff0c;希望对初学者有所帮助。 1. 链接时缺失了相关目标文件&#xff08;.o&#xff09; 测试代码如下&a…

2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结

2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结 教材学习内容总结 重要知识点 I/O&#xff1a;在主存和外部设备&#xff08;例如磁盘存储器、终端和网络&#xff09;之间复制数据的过程。输入操作&#xff1a;从I/O设备复制数据到主存。输出操作&#xff1a;从…

linux 使用VI命令怎么删除输入内容,linux系统vi编辑器常用命令及使用方法。

在linux系统中编辑文档我们常用到vi编辑器。vi编辑器&#xff0c;通常称之为vi,是一种广泛存在于各种UNIX和Linux系统中的文本编辑程序。它的功能十分强大&#xff0c;但是命令繁多&#xff0c;不容易掌握&#xff0c;它可以执行输出、删除、查找、替换、块操作等众多文本操作&…

在react中使用svg的各种骚姿势

开头先抛个可供参考的项目ts-react-webpack4, 或脚手架steamer-react-ts 优势 SVG可被非常多的工具读取和修改(比如vscode)不失真, 放大缩小图像都很清晰SVG文件是纯粹的XML, 也是一种DOM结构使用方便, 设计软件可以直接导出 兼容性 上一张兼容性图表, 或到caniuse.com查看 …