10. Hibernate LazyFetch

1. 前言

本节和大家一起聊聊 Hibernate 中的 Lazy 和 Fetch 的区别,及两者适合的开发场景。通过本节课程的学习,你将了解到:

  • 什么是延迟加载;
  • 延迟加载的意义。

2. 又见 get() 和 load()

Session 对象提供了 2 个方法用来查询 :

  • get() 方法;
  • load()方法。

如果仅以结果为导向,则无法分辨两者的差异性。

两者如同双胞胎,外观虽然差异不大,但其神韵各有千秋。仔细辨别,便能发现属于各自的特征。

真相只有一个,查明真相的手段,也只有一种:让代码回答

2.1 测试 get() 方法

Student stu=null;
try{// 打开事务transaction = session.beginTransaction();//使用get()方法查询学号为1的学生stu=(Student)session.get(Student.class, new Integer(1));System.out.println("--------------输出学生信息------------------");System.out.println(stu.getStuName());transaction.commit();       
} catch (Exception e) {transaction.rollback();
} finally {session.close();
}
System.out.println("***********关闭Session之后******************");
System.out.println(stu.getStuName());

如上测试代码,和上一节课程的 get() 方法测试有区别:

  • 调用 **get()** 方法查询编号为 1 的学生数据,但会在输出学生数据之前先输出一条提示语句,作为标识分割线;
  • 关闭 Session 对象后继续使用查询出来的学生数据。

查看代码运行结果:

select
student0_.stuId as stuId1_0_0_,
student0_.stuName as stuName2_0_0_,
student0_.stuPassword as stuPassw3_0_0_,
student0_.stuPic as stuPic4_0_0_,
student0_.stuSex as stuSex5_0_0_ 
from
Student student0_ 
where
student0_.stuId=?
--------------输出学生信息------------------
Hibernate是老大
***********关闭Session之后******************
Hibernate是老大

结果能说明什么问题呢?

仔细分析输出的日志信息:

  • 调用 get() 方法时,Hibernate 就构建了一条 Sql 语句。说明,调用 get() 方法时,Hibernate 就跑了一趟数据库,并拿到了开发者指定的数据;

  • 关闭 Session 对象后,程序可以继续使用学生数据。说明,通过 get() 方法获得的数据已经保存到程序运行的内存中,不需要再依赖 Session

想说明什么?不想说明什么?只是一个结论。

2.2 测试 load() 方法

把上面测试实例中的 get() 方法换成 load() 方法。

且运行实例:

Student stu=null;
try{// 打开事务transaction = session.beginTransaction();//查询学号为1的学生stu=(Student)session.load(Student.class, new Integer(1));System.out.println("--------------输出学生信息------------------");System.out.println(stu.getStuName());transaction.commit();
} catch (Exception e) {transaction.rollback();
} finally {session.close();
}
System.out.println("***********关闭Session之后******************");
System.out.println(stu.getStuName());

控制台上查看实例使用结果:

--------------输出学生信息------------------
Hibernate: 
select
student0_.stuId as stuId1_0_0_,
student0_.stuName as stuName2_0_0_,
student0_.stuPassword as stuPassw3_0_0_,
student0_.stuPic as stuPic4_0_0_,
student0_.stuSex as stuSex5_0_0_ 
from
Student student0_ 
where
student0_.stuId=?
Hibernate是老大
***********关闭Session之后******************
Hibernate是老大

得到什么结论了吗?

现在开始寻找区别。

不仔细观察,会误判没有什么区别。

而其中有一个很明显的区别就是:

调用 load () 方法时,Hiberante 并没有真正的行动,只有当执行到下面代码时:

System.out.println(stu.getStuName());

Hibernate 才从容不迫地构建 Sql 语句,往数据库跑了一趟,获得数据,再输出数据。

OK!再稍微改动一下测试代码:

//会话对象
Student stu=null;
try {// 打开事务transaction = session.beginTransaction();//查询学号为1的学生stu=(Student)session.load(Student.class, new Integer(1));        transaction.commit();
} catch (Exception e) {transaction.rollback();
} finally {session.close();
}
System.out.println("***********关闭Session之后******************");
System.out.println("--------------输出学生信息------------------");
System.out.println(stu.getStuName());

输出学生信息并不是在调用 load() 方法之后,而是关闭 Session 对象之后,结果又会怎样?猜得出来吗?

把你心中的猜想和下面的输出结果比较一下。

没想到吧,抛异常啦,抛异常没什么大惊小怪的,异常是为了告诉你错误原因。查看异常信息,从中找出原因:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
省略其它若干……

不要望而生畏,都是纸老虎。找出关键词:

could not initialize proxy - no Session

初看字面意思:不能初始化代理,没有 Session 对象。

什么意思?

到了好好解释这个原因的时候:

调用 load( ) 方法时,Hibernate 根本没有如你所期望一样,往数据库跑。但是又不想让你知道它没去,或是怕你担心它是否能完成这份工作。于是 Hibernate 为你提供了一个 代理对象

这里会涉及到代理设计模式!为不影响主题学习,代理设计模式相关内容自己了解一下。

什么是代理对象?

通俗讲就是说外观和开发者所期望的对象一样,但没有实质性数据。
再通俗点,就是一个替身。有其外形,而无内涵。
再通俗讲:哦,你已经明白了。

Hibernate 这是演的哪一出?这不是摆明欺负人吗?

别误会,这是 Hibernate 的善意之举!善意从何谈起呀!别急!

只有当开发者真正需要数据时:

System.out.println(stu.getStuName());

Hibernate 才会构建 Sql 语句,往数据库跑一趟,获得真正的数据。

但是,执行 Sql 语句是一定要在 Session 的生命周期之内,如果:

session.close();

通往数据库的桥梁被拆了。Hibernate 也无能为力,只能以异常的方式告诉你:

no Session!臣妾做不到呀。

测试 get()、load() 方法的输出结果已经表明了两者的差异性:

  • get() 方法言行一致,说出手呀便出手。开发者一调用,便快马加鞭,从数据库中获得数据,保存到学生对象中,只要学生对象在,数据也就在;
  • load() 方法,看起来倒像是说一套,做一套的主。并不会立马行事,而是创建一个学生代理对象,提供和开发者期待的学生数据对象相同的方法接口,不影响开发者调用。只有当开发者真正需要数据时,才会说,好的,我去看一下数据库。

故而,使用 load() 时就需要特别注意,在 Hibernate 取数据库之前,千万别关闭通向数据库的桥梁:Session 对象

Session 家里有 2 个可用于查询的兄弟:

  • get() 是老实人,言行一致。
  • load() 有点小调皮,有时搞点恶作剧,但心思并不坏。如果真正理解它的意图,在特定的环境下,可能会感动到你。

其实两兄弟都很有趣。

3. 延迟加载

延迟加载?不是在聊 get() 和 load() 方法吗,不是聊得好好的嘛!咋的,中场休息呀。

3.1 什么是延迟加载

什么是延迟加载?前面的测试结论已经给出了答案。

使用 Hibernate 获取数据时,有时,Hibernate 并不急着去数据库,而是等到开发者真正需要数据时才会跑一趟数据库。

load() 方法 和 get() 方法的基础区别:

  • load() 支持延迟加载(Lazy);

意思是,别急,你需要时我再去拿数据。如果没有拿到数据,则会抛出异常。

  • get() 方法不支持延迟加载,而是(Fetch),如果没有拿到数据,则返回 null 。

什么时候使用 get(),什么时候使用 load()。只有需求才能告诉你如何权衡,没有绝对的忠告。

3.2 延迟加载的意义

答案很简单:错峰出行,需时索取。

数据库系统的迎接能力终归是有限的。面对同时有很多数据请求时,就会造成拥堵。并不是所有的数据请求会在它的逻辑中立即使用数据。

于是,就可以使用延迟加载技术,暂缓数据请求,真正需要时,或错开数据库系统的访问高峰期后再访问。

在真实的企业级项目中,一个业务逻辑往往是借助于多个组件一起协作完成的。

 

Hibernate 作为数据请求框架,充当数据提供者角色,本身并不处理数据。数据的使用延迟到了数据加工组件之中。

于是,Hibernate 用不着立即造访数据库,先给数据加工组件提供一个代理对象,等数据加工组件真正需要数据时再访问数据库也不迟。

延迟加载是 Hibernate 中的性能优化技术,不要误会它是在使什么小心眼。完全是一番好意。

哲学上讲世界是平衡的,一头变轻,另一头就会变重。总能量消耗不变。

延迟加载技术提供了一种性能优化方式(变轻了),但在还没有真正获取数据之前,不能关闭 Session 对象(生命周期延长,变重了)算是平衡制约吧。

4. 小结

本节课聊到了 Hibernate 中一个很重要的概念:延迟加载,是一种性能优化技术。让开发者在真正需要数据的时候才进入到数据库。

Session 提供的 load() 方法支持延迟加载。但是,千万别以为延迟加载仅仅是 load() 方法的专利。

延迟加载是性能优化技术,Hibernate 在设计时,凡是考虑有必要使用的地方都会有延迟加载的身影。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于深度残差网络迁移学习的浸润性导管癌检测

1. 引言 癌症是一种异常细胞不受控制地分裂损害健康组织的疾病。皮肤或覆盖我们内脏的组织中的癌细胞被称为癌。乳房中的大多数癌是导管癌。侵袭性导管癌(Invasive Ductal Carcinoma, IDC)始于乳管,侵犯乳房周围纤维组织,晚期可通过血液扩散至淋巴结或身…

【鸿蒙学习笔记】位置设置・position・绝对定位・子组件相对父组件

官方文档:位置设置 目录标题 position・绝对定位・子组件相对父组件Row Text position position・绝对定位・子组件相对父组件 正→ ↓ Row Text position Entry Component struct Loc_position {State message: string Hello World;build() {Column() {Co…

二叉树的前、中、后序遍历(递归法、迭代法)leetcode144/94/145

leetcode144、二叉树的前序遍历 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 示例 1: 输入:root [1,null,2,3] 输出:[1,2,3] 示例 2: 输入:root [] 输出:[] 示例 3:…

求职学习day5

安排明天hr面 投一下平安可能。 hr面准备,复习java核心技术,复习java项目。 正视自己,调整心态。 也是很早接触了javaguide但是没有持续学习,项目介绍 | JavaGuide,面试前复习一下感觉还是很有收获的。 还有一些…

【QT】label中添加QImage图片并旋转(水平翻转、垂直翻转、顺时针旋转、逆时针旋转)

目录 0.简介 1.详细代码及解释 1)原label显示在界面上 2)水平翻转 3)垂直翻转 4)顺时针旋转45度 5)逆时针旋转 0.简介 环境:windows11 QtCreator 背景:demo,父类为QWidget&a…

Go语言并发编程-Channel通信_2

Channel通信 Channel概述 不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存 这是Go语言最核心的设计模式之一。 在很多主流的编程语言中,多个线程传递数据的方式一般都是共享内存,而Go语言中多Goroutine通信的主要方案是Cha…

笔记 7 :linux 011 注释,函 bread () , get_hash_table () , find_buffer ()

(57)接着介绍另一个读盘块的函数 bread(): (58)因为 函数 get_blk()大量调用了其它函数,一版面列举不完,故对其调用的函数先行注释:ge…

【驱动程序】霍尔编码器电机_CubeMX_HAL库

【驱动程序】霍尔编码器电机_CubeMX_HAL库 电机型号:MG310 霍尔编码器电机 驱动模块:L298N 接线 注: L298N 12V接线柱位置可以接50V~5V当跳线帽接入时,5V接线柱为5V输出,可以给驱动板供电当跳线帽拔出时&#xff0…

单片机主控的基本电路

论文 1.复位电路 2.启动模式设置接口 3.VBAT供电接口 4.MCU 基本电路

昇思25天学习打卡营第30天 | MindNLP ChatGLM-6B StreamChat

今天是第30天,学习了MindNLP ChatGLM-6B StreamChat。 今天是参加打卡活动的最后一天,经过这些日子的测试,昇思MindSpore效果还是不错的。 ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,具有62亿参数,基于 …

Blender4.2版本正式上线,新版本的5个主要功能!

​Blender刚刚推出了备受瞩目的 Blender 4.2 版本,这款软件专为那些在视觉特效、动画制作、游戏开发和可视化设计领域工作的艺术家们量身打造。作为最新的长期稳定更新,Blender 4.2 不仅稳定可靠,还引入了备受期待的“Eevee Next”实时渲染引…

unity渲染人物模型透明度问题

问题1:有独立的手和衣服的模型,但最终只渲染出来半透明衣服 问题2:透明度贴图是正确的但显示却不正确 这上面两个模型的问题都是因为人物模型是一个完整的,为啥有些地方可以正常显示,有些地方透明度却有问题。 其中…

嵌入式香橙派人工智能AI开发板详细操作与远程聊天实现

大家好,今天给大分享一个OrangePi AIpro(20T)采用昇腾作为主控芯片的开发板,开箱以及对应功能的详细实现。 第一:板子基本介绍 接通电源给对应的开发板上电,观察其中的现象,如下: 注…

Vue 组件插槽 slot 简单例子

https://andi.cn/page/621582.html

GZ032 信息安全管理与评估赛项参考答案-模块1任务二11-20

GZ032 信息安全管理与评估赛项参考答案-模块1任务二 后面的题可能有的地方没有验证但是步骤都对,第13个小题没有做跳过去了等下一期或者最后在做 文章目录 GZ032 信息安全管理与评估赛项参考答案-模块1任务二11.总公司和分公司今年进行IPv6试点,要求总公…

TikTok内嵌跨境商城全开源_搭建教程/前端uniapp+后端源码

多语言跨境电商外贸商城 TikTok内嵌商城,商家入驻一键铺货一键提货 全开源完美运营,接在tiktok里面的商城内嵌,也可单独分开出来当独立站运营 二十一种语言,可以做很多国家的市场,支持商家入驻,多店铺等等…

华为“铁三角模式”在数据类项目中的应用和价值

引言:随着信息技术的飞速发展,企业纷纷踏上数字化转型的道路,希望通过数据分析和智能决策来提升企业竞争力。在这一过程中,数据类项目成为关键,它们旨在构建高效的数据治理和分析平台,为企业决策提供有力支…

【Git远程操作】克隆远程仓库 https协议 | ssh协议

目录 前言 克隆远程仓库https协议 克隆远程仓库ssh协议 前言 这四个都是Git给我们提供的数据传输的协议,最常使用的还是https和ssh协议。本篇主要介绍还是这两种协议。 ssh协议:使用的公钥加密和公钥登录的机制(体现的是实用性和安全性&am…

Linux网络——TcpServer

一、UDP 与 TCP 在现实生活中,Udp 类似于发传单,Tcp 类似于邮局的挂号信服务。 1.1 UDP(用户数据报协议) 无连接:发放传单时,你不需要提前和接受传单的人建立联系,直接把传单发出去。不可靠&…

ffmpeg ffplay.c 源码分析

1 ffplay.c的意义 ffplay.c是FFmpeg源码⾃带的播放器,调⽤FFmpeg和SDL API实现⼀个⾮常有⽤的播放器。 例如哔哩哔哩著名开源项⽬ijkplayer也是基于ffplay.c进⾏⼆次开发。 ffplay实现了播放器的主体功能,掌握其原理对于我们独⽴开发播放器⾮常有帮助…