怎么理解hibernate中的N+1 又怎么去防止这样的情况了?
请给出例子 多谢
2条回答 默认 最新
- iteye_11009 2014-07-18 04:08关注
N + 1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
所谓的N+1是在查询的时候发出了N+1条sql语句
1: 首先发出一条查询对象id列表的sql
N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句
list和iterate的区别?
list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,也就是当Session关闭的时候该Session所管理的一级缓存也会立即被清除。
@Test
public void test01() {
Session session = null;
try {
/**
* 此时会发出一条sql取出所有的学生信息
/
session = HibernateUtil.openSession();
List ls = session
.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).list();
Iterator stus = ls.iterator();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
/*
* id=1的Student对象已经在session的缓存(一级缓存)中,
* 此时就不会发sql去取Student
/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
try {
session = HibernateUtil.openSession();
/*
* 上一个Session已经关闭,此时又得重新取Student
*/
Student stu = (Student)session.load(Student.class, 1);
System.out.println(stu.getName()+",---");
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}典型的N+1问题
如果使用iterator方法返回列表,对于hibernate而言,它仅仅只是发出取id列表的sql
在查询相应的具体的某个学生信息时,会发出相应的SQL去取学生信息,这就是典型的N+1问题
@Test
public void test02() {
Session session = null;
try {
session = HibernateUtil.openSession();
Iterator stus = session.createQuery("from Student")
.setFirstResult(0).setMaxResults(50).iterate();
for(;stus.hasNext();) {
Student stu = stus.next();
System.out.println(stu.getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
HibernateUtil.close(session);
}
}存在iterator的原因是
有可能会在一个session中查询两次数据,如果使用list每一次都会把所有的对象查询上来,而是要iterator仅仅只会查询id,此时所有的对象已经存储在一级缓存(session的缓存)中,可以直接获取。如下例:
/**- 此时会发出一条sql取出所有的学生信息 / session = HibernateUtil.openSession(); List ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); Iterator stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } /*
- 使用iterate仅仅只会去Student的id,
- 此时Student的数据已经在缓存中,所以不会在出现N+1 / stus = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).iterate(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } 如果上面不使用iterate,而直接使用list,会查出很多数据 /*
- 此时会发出一条sql取出所有的学生信息 / session = HibernateUtil.openSession(); List ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); Iterator stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); } /*
- 会发出SQL取完整的学生对象,占用内存相对较多 */ ls = session.createQuery("from Student") .setFirstResult(0).setMaxResults(50).list(); stus = ls.iterator(); for(;stus.hasNext();) { Student stu = stus.next(); System.out.println(stu.getName()); }
再不清楚看看领悟书生 免费的学习网站
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报
悬赏问题
- ¥15 谁会P4语言啊,我想请教一下
- ¥20 win11无法启动 持续蓝屏且系统还原失败,无法开启系统保护
- ¥15 哪个tomcat中startup一直一闪而过 找不出问题
- ¥15 这个怎么改成直流激励源给加热电阻提供5a电流呀
- ¥50 求解vmware的网络模式问题 别拿AI回答
- ¥24 EFS加密后,在同一台电脑解密出错,证书界面找不到对应指纹的证书,未备份证书,求在原电脑解密的方法,可行即采纳
- ¥15 springboot 3.0 实现Security 6.x版本集成
- ¥15 PHP-8.1 镜像无法用dockerfile里的CMD命令启动 只能进入容器启动,如何解决?(操作系统-ubuntu)
- ¥30 请帮我解决一下下面六个代码
- ¥15 关于资源监视工具的e-care有知道的嘛