一些关于Hibernate延迟加载的误区
最近面试别人,正好出的笔试题中有道关于Hibernate延迟加载的问题,聊天过程中发现很多人对Hibernate的延迟加载有些理解误区,写 些东东在这里,希望对大家有所帮助。
首先是第一个误区:延迟加载只能作用于关联实体
看到这个是不是在想:非关联实体延迟加载有什么用?
为 了解答上面这个问题,我们可以先考虑另一个问题:Hibernate Session的get和load方法有什么区别?
如果你的回答是:当方法参 数为数据库不存在的id时,get会返回null,load会抛出异常,那么恭喜你,进入了第二个误区
如果此时你还想补充一 下:load会从缓存中取出数据而get不会,再次恭喜,进入第三个误区
如果你在上面三个误区中有一个踏入了,那么我敢打赌,你一定是被网上那些半吊子的工程师们写的博客给戕害了。。。。
此时是不是很愤怒?这些 长久以来你牢记在心的Hibernate的特性原来都是浮云。。。。
呵呵,接下来我们一个个来走出这些误区。
Mop上无图无真相,我们这里无码无真相——不要误会,我是说代码
首先看看第二个误区:当方法参数为数据库不存在的id时,get会返回null,load会抛出异常
如果你现在想说:没错啊,我自己就测试 过,get确实返回了null,load确实抛出了异常。
那么请回答:load是在执行load语句时抛出异常的吗?为什么?如果你答不上来,那 么接着看下面的代码吧:
@Test(expected = IllegalArgumentException.class)
public void 延迟加载() throws Exception {
// 启动
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = (User)session.load(User.class, 100L); // 不存在的ID
try {
user.getName();
} catch (ObjectNotFoundException ex) {
// 命中数据库发现没有对象即抛出ObjectNotFoundException异常
throw new IllegalArgumentException("随便抛出一个不可能的异常");
}
tx.commit();
session.close();
}
由这个test case我们可以知道load并不是在执行时就马上抛出不存在数据的异常的(ObjectNotFoundException),这是为什么呢?再看代 码:
@Test(expected = IllegalArgumentException.class)
public void 延迟加载() throws Exception {
// 启动
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = (User)session.load(User.class, 100L); // 不存在的ID
Assert.assertTrue(user instanceof HibernateProxy);
user.getId(); // 由于ID是不被延迟加载的属性,因此不会抛出异常
try {
Hibernate.initialize(user); // 此时才会触发命中数据库
//user.getName();
} catch (ObjectNotFoundException ex) {
// 命中数据库发现没有对象即抛出ObjectNotFoundException异常
throw new IllegalArgumentException("随便抛出一个不可能的异常");
}
tx.commit();
session.close();
}
看高亮的几行,代码已经把问题说得很清楚了,get和load最大的区别是(假设缓存皆空的情况):get是立即命中数据库去查询 这条记录,而load则是直接返回一个代理对象(HibernateProxy)而不命中数据库,换句话来说load是为单个对象进行了延迟加载,如果你 不去访问这个对象的除ID外的属性,即使目标记录不存在它也永远都不会抛出异常。由于load不立即命中数据库,它确实有一定几率提高 效率
OK,我想上面一段话应该可以解释第一和第二个误区了,那么第三个误区呢?
再看代码
@Test
public void get和load一级缓存测试() throws Exception {
// 启动
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// 验证load在缓存为空的情况下是否会使得加载的对象过一级缓存
User user1 = (User)session.load(User.class, 1L); // 存在的ID,此时虽然没有解开Proxy但已经进入缓存
Assert.assertTrue(user1 instanceof HibernateProxy);
Hibernate.initialize(user1); // 解开Proxy,会触发命中数据库操作
User user3 = (User)session.get(User.class, 1L);
Assert.assertTrue(user3 instanceof HibernateProxy); // 即使使用get,但由于缓存中存储的是一个Proxy,所以这里得到的也是Proxy
Hibernate.initialize(user3); // 解开Proxy,但不会命中数据库
// 验证在load一个不存在的ID后,不解开然后get
User user4 = (User)session.load(User.class, 100L); // 不存在的ID,仍然将Proxy进入缓存
Assert.assertTrue(user4 instanceof HibernateProxy);
//Hibernate.initialize(user3); // 不解开Proxy
try {
session.get(User.class, 100L); // 得到Proxy,命中数据库尝试解开Proxy,由于ID不存在因此抛出异常
Assert.fail("ID不存在所以会出错,不会执行本条");
} catch (ObjectNotFoundException ex) {
}
// 清空缓存
session.clear();
// 验证缓存为空的情况下get是否为Proxy
User user6 = (User)session.get(User.class, 1L); // 命中数据库,直接将组装完成的User实体进入缓存
Assert.assertTrue(!(user6 instanceof HibernateProxy));
// 验证get从缓存中取出对象
User user7 = (User)session.get(User.class, 1L);
Assert.assertTrue(!(user7 instanceof HibernateProxy)); // 缓存中是真实的User对象,get取出的就是真实的User对象
// 验证load是否从一级缓存取数据
User user8 = (User)session.load(User.class, 1L);
Assert.assertTrue(!(user8 instanceof HibernateProxy)); // 缓存中是真实的User对象,load取出的也是真实的User对象
tx.commit();
session.close();
}
相信注释已经足够详细了,打开hibernate.show_sql,总共命中三次数据库(执行SQL),分别在高亮的三行处,其余的全是从缓存中 取数据。
而且值得注意的一点是,如果对象是从load加载到缓存中的,那么不论get还是load获取出来的都是一个Proxy,如果没有被解开 过,那么get会尝试解开它;如果对象是从get加载到缓存中的,那么load和get取出来都会是真实的实体对象。也就是说,get和 load都会从缓存中取出对象,且取出的对象总是保持其第一次加载时的状态(load为Proxy,get为真实对象)
以上代码是一级缓存的验证,想验证二级缓存只需要从Hibernate中开启二级缓存再次运行代码即可
一些关于Hibernate延迟加载的误区的更多相关文章
- [转]Hibernate延迟加载与opensessioninviewFilter
原文地址:http://blog.csdn.net/a19881029/article/details/7916702 hibernate延迟加载: 一个person对应多个school,使用hibe ...
- Hibernate延迟加载Lazy
Hibernate延迟加载Lazy 延迟加载(lazy load)又称为懒加载,延迟加载的机制是为了避免一些无谓性能的开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作 如 ...
- Hibernate学习--hibernate延迟加载原理(动态代理)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- 【转】hibernate 延迟加载
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- hibernate 延迟加载深入分析(persistentSet的延迟加载)
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- Hibernate 延迟加载 分析
出处:http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/#icomments Hibernate 的延迟加载(lazy load ...
- Hibernate学习--hibernate延迟加载原理-动态代理(阿里电面)
在正式说hibernate延迟加载时,先说说一个比较奇怪的现象吧:hibernate中,在many-to-one时,如果我们设置了延迟加载,会发现我们在eclipse的调试框中查看one对应对象时,它 ...
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- SSH项目web.xml文件的常用配置【struts2的过滤器、spring监听器、解决Hibernate延迟加载问题的过滤器、解决中文乱码的过滤器】
配置web.xml(struts2的过滤器.spring监听器.解决Hibernate延迟加载问题的过滤器.解决中文乱码的过滤器) <!-- 解决中文乱码问题 --> <filter ...
随机推荐
- 矩阵乘法分配律+bitset优化——hdu4920
因为是模3,所以把原矩阵拆成两个01矩阵,然后按分配律拆开分别进行矩阵乘法,行列用bitset来存进行优化即可 注意 int bitset<int>::count() 函数可以统计bits ...
- 在Panel上绘图的实现
近期制作了FDS的一个建模工具,由于知识有限,做出的效果是2D的.昨天上课的时候看老师画一个长方体,突然想到,为什么不给普通的2D图形加画上几条直线,就能实现2D图形的3D视觉效果呢?于是回来马上做了 ...
- Feign远程调用源码阅读
- NX-二次开发删除对象UF_OBJ_delete_object
NX9+VS2012 #include <uf.h> #include <uf_curve.h> #include <uf_obj.h> UF_initialize ...
- [JZOJ 5814] 树
题目:从u到v经过多少条边. 思路: 考虑他是怎么走的?? 从\(u\)到\(v\)一定是\(fa[u]\),\(fa[fa[u]]\),反正就是走\(LCA\),那么如果算出每个点到父亲的期望步数, ...
- iOS开发UIResponder简介API
#import <Foundation/Foundation.h> #import <UIKit/UIKitDefines.h> #import <UIKit/UIEve ...
- SPSS单一样本的T检验
SPSS单一样本的T检验 如果已知总体均数,进行样本均数与总体均数之间的差异显著性检验属于单一样本的T检验.在SPSS中,单一样本的T检验由"One-Sample T Test"过 ...
- Invalid Rom
问题描述 原因说明 这多半是因为单片机超频被锁,就是HSE_VALUE设置的与实际值不一致,容易造成这个问题. 解放方法 1. 先正确配置 HSE_VLAUE, 看HSE_VALUE 修改问题. 2. ...
- AtCoder ABC 128E Roadwork
题目链接:https://atcoder.jp/contests/abc128/tasks/abc128_e 题目大意 在一条路上(这条路可以看做坐标轴 x 轴从 0 开始方向为正无穷方向的射线),有 ...
- Python print命令/ 解压序列
Python 命令参数 print 命令 : #默认的print是有个 空格,和换行的 # print(sep= ' ') # print(end = '/n') a = 'sunjinchao' ...