zoukankan      html  css  js  c++  java
  • hibernate的list和iterate的区别


    一。Hibernate的N+1条SQL查询问题-------Iterate

    ibernate使用session.createQuery(hql)查询数据的时候,有两种查询方式:

    1、一种是只查询一次,将所有要查询的数据都查询出来,后面直接取数据就可以了;

          获取方式:session.createQuery(hql).list()。

          一次性加载的数据多,效率低,用于例如商品信息展示。适用于展示所有信息。

    2、另一种是先查询一次,将主键查询出来,后面需要数据的时候,根据主键一条一条的取数据。

           获取方式:session.createQuery(hql).iterate()。

          用于新闻列表展示。当需要查看新闻内容时,在去根据主键查询具体内容数据。

          适用于仅仅展示某一条或几条信息。

    二、先介绍一下java中的缓存系统JCS(java cache system)
     1、JCS(Java Caching System)是一个对象Cache,它可以把Java对象缓存起来,提高那些访问频率很高的Java对象的存取效率。JCS是按照对象的唯一标示来存取对象的,比如说可以按照对象的    hashCode来存取。
     2、对于Hibernate来说,可以利用JCS来缓存查询结果,这样当下次访问同样的数据,就无须去数据库取,直接从JCS中取出来,加快了查询速度。 
     3、当Hibernate使用List或者Iterator方式来第一次读取数据的时候,JCS是空的,此时不管是List方式还是Iterator方式都会往JCS里面填充查询出来的持久对象,
     例如:  select c from Cat as c 
      而 select c.id, c.name from Cat as c 这种HQL语句不构造PO,因此不会去填充JCS。 
     4、从JCS中区数据:
      从JCS中取数据是按照对象的唯一标示来存取的,而对于PO持久对象来说,唯一标示就是主键,因此Hibernate首先必须获得主键列表,然后根据主键列表挨个判定,看这个持久对象究竟是在JCS里面还是在数据库里面,假如在JCS里面,那么按照主键取,假如在数据库,那么发送sql取。
     5、这里先介绍一下Iterator可以使用JCS,而List不能。上面说了,用JCS之前,要先获得持久对象的主键,才能去JCS里面取持久对象,而获得主键列表必须去数据库中取得,这一步是没有办法缓冲的。

     二、list和iterate查询数据不同形式:

    1、Hibernate Iterator的查询本身是分为两步的:存在N+1问题
      ==> select id from cat //一条
      ==> select * from cat where id = ?  //n条 
      解析:第一步,去数据库中取主键列表,
    第二步,按照主键一个一个取数据。假如不用JCS的话,那么从数据库中取出n条记录就需要n+1次sql查询,
     假如使用了JCS,他会先到JCS里面去看看,按照主键去找持久对象,假如有了,直接拿出来用,假如没有,那么只好去数据库中取得,然后再把它填到JCS里面去。 2、而Hibernate List方式: ==> select * from cat 它是JDBC的简单封装,一次sql就把所有的数据都取出来了,它不会像Iterator那样先取主键,然后再取数据,因此List无法利用JCS。
    不过List也可以把从数据库中取出的数据填充到JCS里面去。
     当再次使用list查询数据的时候,仍然要发送sql去数据库中读取数据,证明list()方法不使用缓存

    第一步,去数据库中取主键列表,第二步,按照主键一个一个取数据。当然了,我们现在可以看出来,Iterator方式下假如不用JCS的话,那么从数据库中取出n条记录就需要n+1次sql查询,这是非常可怕的事情,因此假如在没有使用JCS的情况下,你又必须一次去取大量数据,应该避免使用Iterator。  

    而Iterator的第一次sql是取主键列表,这个时间消耗是非常少的,假如使用了JCS,那么每次查询仍然要不可避免的去发送一次sql: select id from cat 去取主键列表,然后呢? 然后Iterator就不会那么傻了,他会先到JCS里面去看看,按照主键去找持久对象,假如有了,直接拿出来用,假如没有,那么只好去数据库中取得,然后再把它填到JCS里面去。  

    因此可以看出来,JCS有点类似一个内存中的简单对象数据库, Iterator的第一次sql取主键列表是必须要到数据库里面取的,取得了主键这把钥匙以后,Iterator会首先尝试开JCS这把锁,打得开就直接进去,假如打不开,就只好去开数据库这把锁了。  

    而Hibernate List方式是JDBC的简单封装,一次sql就把所有的数据都取出来了,它不会像Iterator那样先取主键,然后再取数据,因此List无法利用JCS。不过List也可以把从数据库中取出的数据填充到JCS里面去。  

    最佳的方式:第一次访问使用List,快速填充JCS,以后访问采用Iterator,充分利用JCS。 

    mikeho write:  

    那JCS是如何和Database保持同步的?  

    robbin write:  

    jcs.default.elementattributes.MaxLifeSeconds=240(最大缓冲时间)  

    超时做作废,另外你在程序里面也可以clear JCS cache 

    三、list和iterate返回的对象分析:
     list()中返回的是实体对象, 而iterate()中返回的对象是代理对象.

    https://blog.csdn.net/liumuqi110/article/details/4625055

    四、list和iterate对缓存的使用情况

    1、iterate会查询session缓存、2级缓存, list只会使用查询缓存(如果查询缓存开启的话)
     2、当查询缓存开启时,对iterate和list查询语句的影响
      查询缓存:将query 的cache打开的话,缓存的是query本身,以hql 生成的 sql ,再加上参数,分页等信息做为key值, 而不是query的结果.query的结果是放在session的cache中和二    级cache中,和query的cache是有区别的.
      配置查询缓存:打开query的cache:1,在config中设置 hibernate.cache.use_query_cache = true,2,在创建query后query.setCacheable(true);
     
     下面是打开query的cache后用list 的几个场景:
      1:

      Query q1 = s1.createQuery("from com.test.hb.Hbtest h where h.id<3");
      q1.setCacheable(true);

      Query q2 = s1.createQuery("from com.test.hb.Hbtest h where h.id=1");
      q2.setCacheable(true);

      从逻辑上将,第一个query查询出的值应该包含第二个query,但是第二个query不会用query的cache,而是会从新查询数据库.

      2:
      Query q1 = s1.createQuery("from com.test.hb.Hbtest h where h.id=1");
      q1.setCacheable(true);

      Query q2 = s1.createQuery("from com.test.hb.Hbtest h where h.id=1");
      q2.setCacheable(true);

      两个query一模一样,第二个query会用query的cache,不会从新查询数据库.

      3:
      Query q1 = s1.createQuery("from com.test.hb.Hbtest h where h.id=?").setInteger(0, 1);
      q1.setCacheable(true);

      Query q2 = s1.createQuery("from com.test.hb.Hbtest h where h.id=?").setInteger(0, 1);
      q2.setCacheable(true);

      两个query是一样的,第二个query会用query的cache,不会从新查询数据库.

      4,
      Query q1 = s1.createQuery("from com.test.hb.Hbtest h where h.id=?").setInteger(0, 1);
      q1.setCacheable(true);

      Query q2 = s1.createQuery("from com.test.hb.Hbtest h where h.id=?").setInteger(0, 2);
      q2.setCacheable(true);

      从逻辑上将,两个query是不一样的,第二个query不会用query的cache,而是会从新查询数据库.

     query的cache打开后用iterator 的场景:
      query的cache不会缓存第一次针对ID的SQL,后面iterator的时候,会根据session中的cache决定数据库的访问.可以说query的cache对iterator方式没有影响.
      **如果cache的是native SQL,两个query的sql 语句必须是一样的(包括大小写).如下面的例子会访问两次数据库.
      本地sql:
      Query q1 = s1.createSQLQuery("select val from Hbtest H where H.id=1");
      Query q2 = s1.createSQLQuery("select val from Hbtest h where h.id=1");

      而对于HQL则不会,HQL会被hibernate翻译成SQL,在这过程会'识别'出是不是相同的SQL.下面的例子只访问1次数据库.
      Query q1 = s1.createQuery("from com.test.hb.Hbtest h1 where h1.id=1");
      Query q2 = s1.createQuery("from com.test.hb.Hbtest h where h.id=1");

    五、iterate和list的使用情况:
     iterate:
     s1 = sessionFactory.openSession();
     Query q = s1.createQuery("from com.test.hb.Hbtest h where h.id<2");
     Iterator itr = q.Iterator(); 
     while(itr.hasNext()){
      Hbtest tbo = (Hbtest)itr.next();
     }

     list:
     iterate:
     s2 = sessionFactory.openSession();
     Query q = s2.createQuery("from com.test.hb.Hbtest h where h.id<2");
     List ls = q.list(); 
     int i=0;
     while(i<ls。size()){
      Hbtest tbo = (Hbtest)ls。get(i);
      i++;
     }

     两者相结合的使用形式:
     s1 = sessionFactory.openSession();
     Query q = s1.createQuery("from com.test.hb.Hbtest h where h.id<2");
     List l = q.list(); 
     Iterator itr = l.iterator();
     while(itr.hasNext()){
         Hbtest tbo = (Hbtest)itr.next();
     }
     list()方法会一次取出所有的结果集对象,而且他会依据查询的结果初始化所有的结果集对象。如果在结果集非常庞大的时候会占据非常多的内存,甚至会造成内存溢出的情况发生。
     iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。一次在访问中可以控制缓存中对象的数量,以避免占用过多的缓存,导致内存溢出情况的发生。

     原文链接:http://www.linuxidc.com/Linux/2012-01/52003.htm

    实际测试

    一共有44条记录:

    只有Iterator:44+1 条记录

        @Override
        @Transactional
        public Device getDeviceById(String deviceId) {
            // TODO Auto-generated method stub
            Session session = sessionFactory.getCurrentSession();
            String sql1 = "FROM Device WHERE area = '省中心'";
            
            Query query1 = session .createQuery(sql1);
            //query1.setCacheable(true);
                
            
            Iterator<Device> its = query1.iterate();
            Device device =null;
            
            while (its.hasNext()) {
                 device = its.next();        
                 System.out.println(device);
            }        
            return device;
        }

    iterate 和 list结合

    1条sql,

    @Override
        @Transactional
        public Device getDeviceById(String deviceId) {
            // TODO Auto-generated method stub
            Session session = sessionFactory.getCurrentSession();
            String sql1 = "FROM Device WHERE area = '省中心'";
            
            Query query1 = session .createQuery(sql1);
            //query1.setCacheable(true);
            
            List<Device> devices = query1.list();
            
            Iterator<Device> its = devices.iterator();
            Device device =null;
            
            while (its.hasNext()) {
                 device = its.next();        
                 System.out.println(device);
            }
    
            
            return device;
        }
  • 相关阅读:
    CURL 命令
    进入容器时给容器root权限
    Nginx性能优化功能- Gzip压缩(大幅度提高页面加载速度)
    Linux防火墙Firewall和Iptables的使用
    Linux 下统计文件的个数
    linux命令: wc命令
    Java static关键字详解
    Jmeter 定时器
    Java数据类型转换:强制类型转换+自动类型转换
    Jmeter中正则表达式提取器使用详解
  • 原文地址:https://www.cnblogs.com/lukelook/p/11076747.html
Copyright © 2011-2022 走看看