一些关于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中开启二级缓存再次运行代码即可
转载于:https://www.cnblogs.com/writeLessDoMore/p/6732383.html
一些关于Hibernate延迟加载的误区相关推荐
- hibernate 延迟加载问题探讨
延迟初始化错误是运用Hibernate开发项目时最常见的错误.如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于Session范围内)时,才能初始化它.如果在 ...
- hibernate 延迟加载(转载)
http://blog.csdn.net/xc635960736/article/details/7049863(未找到原始的引文) Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默 ...
- IT忍者神龟之hibernate 延迟加载问题探讨
关于 lazy 机制: 延迟初始化错误是运用 Hibernate 开发项目时最常见的错误.如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于S ...
- hibernate 延迟加载
Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hiberna ...
- hibernate延迟加载(get和load的区别)
在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对 ...
- hibernate延迟加载lazy的原理,以及为什么session关闭会报错
2019独角兽企业重金招聘Python工程师标准>>> 通过将class的lazy属性设置为true,来开启实体的延迟加载特性. 如果我们运行下面的代码: User user=(Us ...
- Hibernate延迟加载
1.类级别的延迟加载 1. 是通过session.load方法实现的 在映射文件中: <class name="cn.itcast.hibernate71 ...
- hibernate延迟加载(get和load的区别)(转)
在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对 ...
- Hibernate 延迟加载(一)
1 延迟加载策略 Hibernate 的延迟加载(lazy load)是一个被广泛使用的技术.这种延迟加载保证了应用只有在需要时才去数据库中抓取相应的记录.通过延迟加载技术可以避免过多.过早地加载数据 ...
最新文章
- 重磅!谷歌面试官亲自分享:Google面试技巧
- 怎么样配置交换机TRUNK
- 程序员面试题精选100题(48)-二叉树两结点的最低共同父结点[数据结构]
- 数据结构之优先队列--二叉堆(Java实现)
- samba and AD
- Mybatis 与java 类型 对应表
- Tyche 2147 旅行
- Django-manage.py
- 《怎样解题》思维导图
- fzu 2122 又见LKity
- OpenCV55:高动态范围成像|High Dynamic Range (HDR)
- 【第三方互联】微博2、创建新浪微博(sina)互联应用
- 高通驱动开发参考(二)
- 让每块火腿都留下“数字痕迹”西媒:区块链将用于保护食品安全
- 普拉图和施泰纳问题的实验解法
- linux无损扩空间,linux无损扩容的方法
- 从未在一起更让人遗憾_更加遗憾的是,从未在一起过
- 暑假实习证明格式范文
- 基于联邦学习的多源异构数据融合算法 Multi-Source Heterogeneous Data Fusion Based on Federated Learning
- 蓝桥杯【第13届省赛】Python 实现