hibernate缓存机制与N+1问题
在项目中遇到的趣事
本文基于hibernate缓存机制与N+1问题展开思考,
先介绍何为N+1问题
再hibernate中用list()获得对象:
/**
* 此时会发出一条sql,将30个学生全部查询出来
*/
List<Student> ls = (List<Student>)session.createQuery("from Student")
.setFirstResult(0).setMaxResults(30).list();
Iterator<Student> stus = ls.iterator();
for(;stus.hasNext();)
{
Student stu = (Student)stus.next();
System.out.println(stu.getName());
}
控制台输出:
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?
如果通过list()方法来获得对象,毫无疑问,hibernate会发出一条sql语句,将所有的对象查询出来,这没什么问题。
用iterator()这种情况:
/**
* 如果使用iterator方法返回列表,对于hibernate而言,它仅仅只是发出取id列表的sql
* 在查询相应的具体的某个学生信息时,会再发出相应的SQL去取学生信息
* 这就是典型的N+1问题
* 存在iterator的原因是,有可能会在一个session中查询两次数据,如果使用list每一次都会把所有的对象查询上来
* 而是要iterator仅仅只会查询id,此时所有的对象已经存储在一级缓存(session的缓存)中,可以直接获取
*/
Iterator<Student> stus = (Iterator<Student>)session.createQuery("from Student")
.setFirstResult(0).setMaxResults(30).iterate();
for(;stus.hasNext();)
{
Student stu = (Student)stus.next();
System.out.println(stu.getName());
}
在执行完上述的测试用例后,我们来看看控制台的输出,看会发出多少条 sql 语句:
Hibernate: select student0_.id as col_0_0_ from t_student student0_ limit ?
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
张一
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
肇庆
Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?
桑耳
.........
我们看到,当如果通过iterator()方法来获得我们对象的时候,hibernate首先会发出1条sql去查询出所有对象的 id 值,当我们如果需要查询到某个对象的具体信息的时候,hibernate此时会根据查询出来的 id 值再发sql语句去从数据库中查询对象的信息,这就是典型的 N+1 的问题。(简单来说就是会有多一次去数据库中查询,解决思路很简单,把那多余的一次的查询不在数据库中执行就可以了)
那么这种 N+1 问题我们如何解决呢,其实我们只需要使用 list() 方法来获得对象即可。
但是既然可以通过 list() 我们就不会出现 N+1的问题,那么我们为什么还要保留 iterator()这种形式呢? 嗯..............存在即合理。想想有没有特殊的情况能发挥 iterator()的优势????
如果我们需要在一个session当中要两次查询出很多对象,此时我们如果写两条 list()时,hibernate此时会发出两条 sql 语句,而且这两条语句是一样的,
但是我们如果第一条语句使用 list(),而第二条语句使用 iterator()的话,此时我们也会发两条sql语句,但是第二条语句只会将查询出对象的id,所以相对应取出所有的对象而已,显然这样可以节省内存和减少与数据库的交互(提升效率),而如果再要获取对象的时候,因为第一条语句已经将对象都查询出来了,此时会将对象保存到session的一级缓存中去,所以再次查询时,就会首先去缓存中查找,如果找到,则不发sql语句了。这里就牵涉到了接下来这个概念:hibernate的一级缓存。
二、一级缓存(session级别)
我们来看看hibernate提供的一级缓存:
/**
* 此时会发出一条sql,将所有学生全部查询出来,并放到session的一级缓存当中
* 当再次查询学生信息时,会首先去缓存中看是否存在,如果不存在,再去数据库中查询
* 这就是hibernate的一级缓存(session缓存)
*/
List<Student> stus = (List<Student>)session.createQuery("from Student")
.setFirstResult(0).setMaxResults(30).list();
Student stu = (Student)session.load(Student.class, 1);
我们来看看控制台输出:
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?
我们看到此时hibernate仅仅只会发出一条 sql 语句,因为第一行代码就会将整个的对象查询出来,放到session的一级缓存中去,当我如果需要再次查询学生对象时,此时首先会去缓存中看是否存在该对象,如果存在,则直接从缓存中取出,就不会再发sql了,但是要注意一点:hibernate的一级缓存是session级别的,所以如果session关闭后,缓存就没了,此时就会再次发sql去查数据库。
try
{
session = HibernateUtil.openSession(); /**
* 此时会发出一条sql,将所有学生全部查询出来,并放到session的一级缓存当中
* 当再次查询学生信息时,会首先去缓存中看是否存在,如果不存在,再去数据库中查询
* 这就是hibernate的一级缓存(session缓存)
*/
List<Student> stus = (List<Student>)session.createQuery("from Student")
.setFirstResult(0).setMaxResults(30).list();
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName() + "-----------");
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
HibernateUtil.close(session);
}
/**
* 当session关闭以后,session的一级缓存也就没有了,这时就又会去数据库中查询
*/
session = HibernateUtil.openSession();
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName() + "-----------");
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ? Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
我们看到此时会发出两条sql语句,因为session关闭以后,一级缓存就不存在了,所以如果再查询的时候,就会再发sql。要解决这种问题,我们应该怎么做呢?若按空间换取时间的思路,那能不能再来一个缓存?这就要我们来配置hibernate的二级缓存了,也就是sessionFactory级别的缓存。
三、二级缓存(sessionFactory级别)(简单介绍)
如果我们只是取出对象的一些属性的话,则不会将其保存到二级缓存中去,因为二级缓存缓存的仅仅是对象。
由于学生对象已经缓存在二级缓存中了,此时再使用iterate来获取对象的时候,首先会通过一条取id的语句,
然后在获取去二级缓存中对象时,如果发现就不会再发SQL,这样也就解决了N+1问题 而且内存占用也不多。 万千丛中一点绿, 对象!,对象!,对象! 二级缓存就把第一次对象查询拦截了,解决了N+1问题
hibernate缓存机制与N+1问题的更多相关文章
- 10.hibernate缓存机制详细分析(转自xiaoluo501395377)
hibernate缓存机制详细分析 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论 ...
- 【转 :Hibernate 缓存机制】
转自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html Hibernate 缓存机制 一.why(为什么要用Hibernate缓存 ...
- hibernate缓存机制详解
hiberante面试题—hibernate缓存机制详解 这是面试中经常问到的一个问题,可以按照我的思路回答,准你回答得很完美.首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再 ...
- hibernate缓存机制(转)
原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...
- hibernate缓存机制详细分析 复制代码 内部资料 请勿转载 谢谢合作
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- Hibernate 缓存机制
一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...
- hibernate缓存机制详细分析
转自:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级 ...
- Hibernate 缓存机制二(转)
感谢:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...
- Hibernate 缓存机制(转)
一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...
随机推荐
- 面试必备:GET和POST的用法和区别
版权声明一:本文为博主原创文章,转载请附上原文出处链接和本声明.版权声明二:本网站的所有作品会及时更新,欢迎大家阅读后发表评论,以利作品的完善.版权声明三:对不遵守本声明或其他违法.恶意使用本网内容者 ...
- RS chap2:利用用户行为数据
一.用户行为数据简介 1.用户行为在个性化推荐系统中分为两种: (1)显式反馈行为:包括用户明确表示对物品喜好的行为. (2)隐式反馈行为:不能明确反应用户喜好的行为. (3)显式反馈行为和隐式反馈行 ...
- varchar nvarchar 设计长度时 设计成 (2^n)-1 的好处
这个问题想说已久就是博没共享出来 首先提出个问题 CREATE TABLE `test` ( `a` ) DEFAULT NULL, ) ENGINE=InnoDB DEFAULT CHARSET=u ...
- Redis5以上版本伪集群搭建(高可用集群模式)
redis集群需要至少要三个master节点,我们这里搭建三个master节点,并且给每个master再搭建一个slave节点,总共6个redis节点,这里用一台机器(可以多台机器部署,修改一下ip地 ...
- SpringMVC基础01——SpringMVC的知识结构体系
1.前言 目前在各大互联网公司使用最热门的技术莫过于SpringBoot以及在其基础之上的SpringCloud,然而学习这些技术的基础在于学好Spring和SpringMVC,准确来说SpringM ...
- id 显示用户与用户组的信息
id 显示用户与用户组的信息 1.命令功能 id显示指定用户的用户ID和组ID等信息. 2.语法格式 id option username 参数说明 选项 选项说明 -gx 显示用户组ID -G ...
- chrome 开发者工具 - local overrides
使用chrome 作为本地网络服务 chrome 65+ 新功能, 使用我们自己的本地资源覆盖网页所使用的资源,可以使用本地css文件覆盖网页的css文件,修改样式. 类似的,使用DevTools的工 ...
- Kendo UI for jQuery使用教程:支持Web浏览器
[Kendo UI for jQuery最新试用版下载] Kendo UI目前最新提供Kendo UI for jQuery.Kendo UI for Angular.Kendo UI Support ...
- 如何用redis正确实现分布式锁?
先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...
- Notepad++技巧
1 正则表达式的查找和替换,例如删除每行开始的数字ctrl+H, ^\d\d\d:null 2 删除所有的空行: TextFX插件->Edit->Delete Blank Lines 3 ...