缓存

2018-09-13  本文已影响0人  Mango_lxh

什么是缓存?

缓存是内存上的一块存储空间,存放经常使用的数据,这块空间的查找效率非常高。
hibernate把外存上的空间也作为缓存(二级缓存)

使用缓存目的:提高查找效率

一些结论
查找数据的过程:

先到一级缓存里查找,如果没找到才发sql语句到数据库里查找

iterator使用一级缓存:

先发一条语句到数据库里查找id,根据id再到缓存里查找对应的对象,如果缓存里没找到,再到数据库里找,一旦找到,就把记录放到一级缓存

什么是n+1问题?

一级缓存使用不当,发大量sql语句,造成缓存拥塞

如何避免n+1问题

先list,后iterator

1、一级缓存(session)

一级缓存(缓存实体对象)
一级缓存很短和session的生命周期一致,一级缓存也叫session级的缓存

哪些方法支持一级缓存:
如何管理一级缓存:
如何避免一次性大量的实体数据入库导致内存溢出

2、二级缓存(sessionFactory)

二级缓存也称进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享
二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存

二级缓存的配置和使用:
二级缓存缓存策略
指定缓存策略的方法

在hbm文件中加入: <cache usage="read-only"/>
这个缓存策略的配置一定要加上,否则便不会有缓存的作用, list/iterator等操作的结果将都不会缓存。
注意:在hbm的class配置中添加<cache>配置,表示的是类缓存,如果把这个配置删除,将只缓存ID,不缓存整个对象。(这个时候对list操作,也可能有n+1查询问题)
在hibernate.cfg.xml文件<sessionFactory>标签里面嵌套定义这样的标签:
<class-cache class="com.bjsxt.hibernate.User2" usage="read-only" />

缓存策略的几种形式

缓存有几种形式,可以在映射文件中配置:

  1. read-only(只读,适用于很少变更的静态数据/历史数据)
    这是最简单,也是实用性最好的方法
  2. nonstrict-read-write(不严格读写缓存,如果基本不会发生有两个事务同时修改一个数据的时候,比read-write的性能要好)
  3. read-write(效率一般 ,且支持的缓存产品较少)
    例:hibernate_cache_level_2

3、查询缓存

查询缓存只缓存对象的属性,不缓存对象,如果非要缓存对象,那也只缓存id
只能用list使用,list把属性放到缓存,下次直接到缓存里取
对于list来说,只能通过查询缓存来使用二级缓存。如果使用不当,也会产生n+1问题
关联表发生修改,生命周期结束

对实体对象的结果集只缓存id
查询缓存,只对list 这样的操作会起作用

  1. 查询缓存的生命周期为:
    当前关联的表发生修改,那么查询缓存生命周期结束

  2. 查询缓存的配置和使用:
    (1)查询缓存默认情况下关闭,需要打开。
    可以在hibernate.cfg.xml文件中打开查询缓存 ,如
    <propertyname="hibernate.cache.use_query_cache">true</property>
    (2)在程序中必须手动启用查询缓存,如:
    query.setCacheable(true);
    例:hibernate_query_cache

前提:开启查询缓存,开启二级缓存,使用iterator
前提:关闭二级缓存,开启查询缓存

产生n+1问题
过程:首先发sql语句,到数据库里拿到100个对象,把这个100个对象先放到一级缓存,然后开启查询缓存,把这100个对象的id(主属性)放到查询缓存里。接着关闭session,重新打开一个session。第二次发相同的HQL,它会到查询缓存里取得这100个对象的id,通过这100个对象的id到二级缓存找stu对象,但是现在二级缓存是关闭的,它就只能通过这100个id发100条sql语句到数据库里查找记录,这样就产生了n+1问题

如何解决list产生的n+1问题

只需要开启二级缓存

/**
     * 开启查询缓存,关闭二级缓存
     * 
     * 开启两个session,分别调用query.list查询实体对象
     */
    public void testCache5() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Query query = session.createQuery("select s from Student s");
            //启用查询查询缓存
            query.setCacheable(true);
            
            List students = query.list(); 
            for (Iterator iter=students.iterator();iter.hasNext(); ) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
        
        System.out.println("-------------------------------------");
        
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Query query = session.createQuery("select s from Student s");
            //启用查询查询缓存
            query.setCacheable(true);
            
            //会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存会缓存实体对象的id
            //所以hibernate会根据实体对象的id去查询相应的实体,如果缓存中不存在相应的
            //实体那么将发出根据实体id查询的sql语句,否则不会发出sql使用缓存中的数据
            List students = query.list(); 
            for (Iterator iter=students.iterator();iter.hasNext(); ) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
一些结论
上一篇 下一篇

猜你喜欢

热点阅读