缓存

什么是缓存?

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

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

一些结论
  • 一级缓存和二级缓存只针对对象,查询缓存针对属性,由list使用
  • 缓存使用不当,iterate会造成n+1问题,发大量的sql语句造成缓存拥塞
  • iterator查对象发二条sql语句,查属性发一条
  • 一级缓存只缓存对象,不缓存对象的属性
  • 不同的session有各自的缓存,不能共享
  • hibernate不适合处理批量数据
查找数据的过程:

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

iterator使用一级缓存:

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

什么是n+1问题?

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

如何避免n+1问题

先list,后iterator

1、一级缓存(session)

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

哪些方法支持一级缓存:
  • save()
  • get()
  • load()
  • list() 特点:只放不用
  • iterate(查询实体对象) 特点:先用后放
如何管理一级缓存:
  • session.clear(),session.evict()清除缓存
  • session.flush()将数据持久化到数据库
如何避免一次性大量的实体数据入库导致内存溢出
  • 先flush,再clear
    例:hibernate_cache_level_1

2、二级缓存(sessionFactory)

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

二级缓存的配置和使用:
  • 将echcache.xml文件拷贝到src下
  • 开启二级缓存,修改hibernate.cfg.xml文件
    <property name="hibernate.cache.use_second_level_cache">true</property>
  • 指定缓存产品提供类,修改hibernate.cfg.xml文件
    <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
  • 指定那些实体类使用二级缓存(两种方法)
  • 在映射文件中采用<cache>标签
  • 在hibernate.cfg.xml文件中,采用<class-cache>标签
二级缓存缓存策略
指定缓存策略的方法

在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);
        }
    }
一些结论
  • 查询缓存独立于一级缓存,查询缓存的生命周期比一级缓存长
  • iterator不使用查询缓存
  • list不会使用一级缓存,只能把对象往一级缓存里放
  • list可以直接使用查询缓存,存储属性。如果是对象,存放id到查询缓存
  • list可以往二级缓存里放数据,但是不能直接取,list必须要通过查询缓存使用二级缓存
  • 一旦查询缓存有数据,而关闭了二级缓存,那么这时候使用查询缓存,就会产生n+1问题
  • iterator可以直接使用二级缓存
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容