访存优化_Hibernate事实:多级访存

访存优化

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

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

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

相关文章

基于ARM的字符串拷贝实验(嵌入式系统)

基于ARM的字符串拷贝实验 一,实验目的 1.掌握ARM汇编指令LDR、STR和B等的使用方法,完成较为复杂的存储区访问和程序分支,学会使用条件码。 2.掌握完整的ARM汇编程序结构,具备初步的程序编写能力。 3.掌握ADS1.2集成开发环境的工程建立、编译参数设置、程序编译和调试等…

用于在Synology NAS上测试Spring Boot Web应用程序的JUnit模拟文件

对于将在Synology RS815 NAS上检查备份文件的Spring Boot应用程序&#xff0c;我们希望能够轻松测试此NAS上存储的文件&#xff0c;而不必复制存储在其上的7TB。 理想情况下&#xff0c;我们希望创建相同的文件结构以在Spring开发配置文件中使用Web应用程序&#xff0c;并在J…

根据当天日期计算下一天日期

在日常编程中,经常需要根据当前计算相邻的下一个日期,以下就是这个小算法,直接上代码 // 判断是否是闰年 BOOL IsBissextileYear(int year) {if((((year%4==0) && (year%100)

利用根升余弦滤波器和整数倍内插的多相结构生成含采样频偏的过采样信号

多相表示在多抽样率信号处理中是一种基本方法,使用它可以在实现整数倍和分数倍抽取和内插时提高计算效率。 目前我能想到的应用是信号的成型滤波,需要内插,然后与低通滤波器(成型时为过采样的(根)升余弦波形)进行卷积,这个过程如果直接去计算的话,会有很多多余的乘法操作…

Spring Security with Spring Boot 2.0:使用Servlet堆栈的简单身份验证

Spring安全性是一个很好的框架&#xff0c;可节省开发人员的大量时间和精力。 此外&#xff0c;它还具有足够的灵活性&#xff0c;可以自定义并满足您的需求。 随着spring的发展&#xff0c;spring安全性也使得在项目中设置安全性变得更加容易和自举。 Spring Boot 2.0已经存在…

一种基于最大似然的语音信号混响时间(reverberation time)估计方法的MATLAB实现

基于最大似然(ML)估计器的盲RT估计-引言 混响效应是室内声学的一个重要现象,它是由声音在封闭空间中经多次反射而产生。描述混响效应强弱的一个重要参数是混响时间(Reverberation time, RT),其定义为当声源停止发声后,残余的声能在室内往复反射,经反射面吸收而强度逐渐减弱…

一种基于最大似然的语音信号混响时间(reverberation time)估计方法的纯C语言实现(姊妹篇)

基于最大似然(ML)估计器的盲RT估计-引言 混响效应是室内声学的一个重要现象,它是由声音在封闭空间中经多次反射而产生。描述混响效应强弱的一个重要参数是混响时间(Reverberation time, RT),其定义为当声源停止发声后,残余的声能在室内往复反射,经反射面吸收而强度逐渐减弱…

java与java ee_CapeDwarf – Java EE上的Google App Engine

java与java ee我有很多爱好。 从早期的Java EE规范一路走来&#xff0c;并用Java EE 7进行了“云”之旅&#xff0c;我很好奇看到新宣布的CapeDwarf项目有什么库存&#xff0c;可以在内部引入Google的平台即服务&#xff0c;提供“ Google App Engine ” 。 到目前为止的故事 …

基于MATLAB的高阶(两个二阶级联构成的四阶以及更高阶)数字图形音频均衡器系数计算(可直接用于DSP实现)

引言 前不久,在数字信号处理中需要对音频信号进行滤波,涉及图形均衡器、参数均衡器的设计,下面这个链接给出了一个图形音频均衡器的例子: https://arm-software.github.io/CMSIS_5/DSP/html/group__GEQ5Band.html 这个示例演示如何使用 Biquad 级联函数构建 5 波段图形均衡…

基于MATLAB的曼彻斯特调制与解调实现

引言 曼彻斯特编码也称为相位编码,是一种同步时钟编码技术。通过电平的高低转换来表示“0”或“1”,每一位的中间有一个跳变的动作,这个动作既作时钟信号,又作数据信号,但因为每一个码元都被调成两个电平,所以数据传输速率只有调制速率的1/2,其编码效率为50%。常用于局…

记录一次某MFC软件算法逆向之旅

传说中MFC必杀(并不能通用) 对于直接使用SDK而不使用第三方库的程序,我们要定位到程序的“关键代码”并不困难。通常在CreateWindow函数或DialogBoxParam函数下断点,可以直接获得其主界面的窗口过程或对话框过程。但是对于使用了MFC的程序,我们找到的窗口过程或对话框过程是…

mongodb启动不能锁定_使用MongoDB进行乐观锁定重试

mongodb启动不能锁定在我以前的文章中&#xff0c;我谈到了对MongoDB批处理程序采用乐观锁定的好处。 如我之前所写&#xff0c;乐观锁定异常是可恢复的异常&#xff0c;只要我们获取最新的Entity&#xff0c;我们就会对其进行更新并保存。 因为我们使用的是MongoDB&#xff0…

基于Funcode和VS2017的游戏开发

主要实现了两个小游戏,飞机大战和冰火人历险记,首先用Funcode生成地图文件和VS工程,然后进行编程 头文件 CommonAPI.h #ifndef _COMMON_API_H_ #define _COMMON_API_H_ // #include <windows.h>//===============================

VMWare vijava –“用户名或密码不正确”异常的奇怪情况

在我参与的一个项目中&#xff0c;我们使用yavijava &#xff08;这是vijava的一个分支&#xff09;库与托管我们的VM的vCenter进行交互。 vCenter通过其Web服务端点公开了各种API&#xff0c;这些端点是通过HTTP调用的。 yavijava库具有必要的钩子&#xff0c;允许开发人员在客…

【OFDM系列4】OFDM信号多径信道模型基础知识

多径信道模型(Multipath Channel Scenario) 信道脉冲响应(Channel Impulse Response, CIR) 信道的复基带脉冲响应如下所示 h ( τ ; t ) = ∑ l = 1 L a l ( t

kie-api_KIE-WB / JBPM控制台Ng –配置

kie-api大家好&#xff0c;这是我上一篇文章中有关如何使用jBPM Console的后续文章 。 这篇文章的主要思想是描述为了在您自己的公司中使用jBPM Console NG而需要进行的一些最常见的配置。 但是在讨论技术细节之前&#xff0c;我们将介绍KIE Workbench&#xff08;KIE-WB&#…

【OFDM系列5】单输入单输出OFDM(SISO-OFDM)多径信道迫零(ZF)和最小均方误差(MMSE)均衡器原理和公式推导

OFDM单输入单输出(SISO)迫零(ZF)均衡器 在去除CP之后,第k个子载波上的信号的FFT给出如下 Y k = H k D k + W k ⋯ ( 1 ) Y_k=H_k D_k+W_k\cdots(1)

如何在Tomcat中设置JNDI数据库连接池-Spring教程示例

在Spring和Tomcat中设置JNDI数据库连接池非常容易。 Tomcat服务器文档提供了有关如何在Tomcat 5、6或7中设置连接池的足够信息。在这里&#xff0c;我们将结合使用Tomcat 7和Spring框架在Tomcat服务器中创建连接池并在Spring中使用JNDI代码访问它们。 在上一篇文章中&#xff0…

win10任务栏透明+变窄+免安装

透明设置 在HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced中新建DWORD32位值TaskbarAcrylicOpacity&#xff0c;设置数值为0 重启资源管理器explorer.exe即可 变窄 下载7Taskbar软件7tt_setup.exe&#xff0c;直接解压&#xff0c;对于本…

java vm_Java VM –提防YoungGen空间

java vm正如您从我们以前的面向性能的文章中可能已经看到的那样&#xff0c;运行良好的JVM是实现最佳应用程序性能和稳定性的最重要目标之一。 这样的健康评估通常仅关注主要收集的频率&#xff08;避免&#xff09;或检测内存泄漏的存在。 年轻一代空间或短寿命物体的大小和足…