iteye_15423 2014-07-18 02:34
浏览 244
已采纳

hibernate N+1

怎么理解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+1

    Hibernate的一级缓存是由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()); }

    再不清楚看看领悟书生 免费的学习网站

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥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有知道的嘛