hibernate缓存机制与N+1问题

时间:2023-01-27 14:51:28

在项目中遇到的趣事

本文基于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问题的更多相关文章

  1. 10&period;hibernate缓存机制详细分析(转自xiaoluo501395377)

    hibernate缓存机制详细分析   在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论 ...

  2. 【转 :Hibernate 缓存机制】

    转自:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html Hibernate 缓存机制 一.why(为什么要用Hibernate缓存 ...

  3. hibernate缓存机制详解

    hiberante面试题—hibernate缓存机制详解   这是面试中经常问到的一个问题,可以按照我的思路回答,准你回答得很完美.首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再 ...

  4. hibernate缓存机制(转)

    原文出处:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是 ...

  5. hibernate缓存机制详细分析 复制代码 内部资料 请勿转载 谢谢合作

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  6. Hibernate 缓存机制

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...

  7. hibernate缓存机制详细分析

    转自:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html 在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级 ...

  8. Hibernate 缓存机制二(转)

    感谢:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html 一.why(为什么要用Hibernate缓存?) Hibernate是一个 ...

  9. Hibernate 缓存机制&lpar;转&rpar;

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数 ...

随机推荐

  1. 学习web前端开发基础技术需要掌握:HTML、CSS、JavaScript语言

    1. HTML是网页内容的载体.内容就是网页制作者放在页面上想要让用户浏览的信息,可以包含文字.图片.视频等. 2. CSS样式是表现.就像网页的外衣.比如,标题字体.颜色变化,或为标题加入背景图片. ...

  2. hash-1&period;hash表和hash算法

    1.hash表 哈希表,也叫散列表,是根据关键码(Key)而直接访问的数据结构,也就是它把Key映射到表中一个位置来访问记录,即,把key计算成hashcode,把hashcode存到表中.这个把ke ...

  3. nginx 更新提示端口占用的解决办法

    最近更新ubuntu下的nginx,报了以下的错误, [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use) 可以看到,80 ...

  4. bind&lpar;&rpar; to 0&period;0&period;0&period;0&colon;80 failed &lpar;98&colon; Address already in use&rpar;

    You can kill it using: sudo fuser -k 80/tcp And then try restarting nginx again: service nginx start

  5. 领域驱动设计&lpar;DDD&rpar;

    领域驱动设计(DDD)实现之路 2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱 ...

  6. Tomcat常用参数的配置

    1.修改端口号 Tomcat端口配置在server.xml文件的Connector标签中,默认为8080,可根据实际情况修改. 修改端口号 2.解决URL中文参数乱码 在server.xml文件的Co ...

  7. C&plus;&plus; Primer 有感(标准库set类型)

    set容器只是单纯的键的集合,键必须为一.set容器不支持下标操作,而且没有定义maped_type类型.在set容器中,value_type不是pair类型,而是与key_type类型相同的类型. ...

  8. crypto 简单了解

    阅读前:文章记录crypto库的简单了解,和一些简单的用法,与具体加解密算法的实现无关. 文中例子使用到了node的crypto模块 和  npm sjcl(Stanford Javascript C ...

  9. Spring Boot 2&period;0 热部署指南

    Spring Boot 2.0 支持热部署,实现方法很简单 Spring Boot 2.0 有几种热重载的选项. 推荐的方法是使用spring-boot-devtools 因为它提供了额外的开发时间功 ...

  10. Excel 2010 得到当天的日期&sol;得到一年中的第几周&sol;得到当前一周中的星期几

    =TODAY() ="第"&WEEKNUM(TODAY())&"周" =TEXT(TODAY(),"aaaa") Ctrl ...