文档章节

Hibernate 缓存

architect刘源源
 architect刘源源
发布于 2018/01/22 13:17
字数 2194
阅读 14
收藏 0

概述

Hibernate Cache 对于提高应用的性能是非常有用的。缓存的目标就是减少数据库的查询,从而减少应用的吞吐时间。Hibernate提供如下几种缓存类型

  • 一级缓存(First Level Cache): Hibernate一级缓存是和Session对象紧密关联的。Hibernate一级缓存是默认可用的,并且不能关闭它。但是Hibernate提供了可以删除指定object的方法。任何一个在一个session中缓存的对象在其他session中是不可见的,当session关闭后所有缓存的对象也会丢失。

一级缓存

  • 二级缓存(Second Level Cache): 一级缓存的作用域是Session,而二级缓存的作用域是SessionFactory,这就意味着缓存可以被同一个Session Factory的不同 Session 是可以同时共享缓存实例的。当一个二级缓存被设定,将会发生如下:
    • 如果一个是你实例已经出现在了一级缓存中,则直接返回该实例
    • 如果一个实例在一级缓存中并没有出现,但是相应的实例状态(instace state)被缓存在了二级缓存中,数据将从二级缓存中得到,实例将会被组装和返回。

二级缓存

  • 查询缓存: 将常用的、变化不频繁的查询结果缓存起来。

一级缓存

Session session = sessionFactory.openSession();

//第一次查询数据库
session.get(Employee.class, 1L);

Thread.sleep(2000);

//第二次不查询数据库
session.get(Employee.class, 1L);

session.close();

在同一个session中当对象的实例存在时将会直接返回实例,并不会再次执行查询数据库操作。但是hibernate提供了清除缓对象实例的方法,它们是:

  • clear() : 该方法会清除session一级缓存的所有对象实例
  • evict(Object object) :该方法清除一级缓存的指定对象
Session session = sessionFactory.openSession();

//第一次查询数据库
session.get(Employee.class, 1L);

Thread.sleep(2000);
        
//清除一级缓存
session.clear();
// or session.evict(employee);

//第二次查询数据库,因为一级缓存已经被清除
session.get(Employee.class, 1L);
session.close();

二级缓存

二级缓存的作用域是 Session Factory

可以一个事务产生的二级缓存可以在两一个事务中直接访问到。现在做如下测试(设置缓存策略为只读):

Session session = sessionFactory.openSession();
Session otherSession = sessionFactory.openSession();

Statistics statistics = sessionFactory.getStatistics();
statistics.setStatisticsEnabled(true);


//开启第一个事务
Transaction transaction = session.beginTransaction();

//第一次查询数据库
Employee employee= session.get(Employee.class, 1L);
print("第一次从数据库中查询",statistics);

//第二次从一级缓冲中查询
session.get(Employee.class, 1L);
print("第二次从一级缓冲中查询",statistics);

//清除一级缓存
session.evict(employee);

/从二级缓存中查询
session.get(Employee.class, 1L);
print("从二级缓存中查询",statistics);

//提交第一个事务
transaction.commit();
session.close();

        

//第二个事务
Transaction otherTransaction = otherSession.beginTransaction();

otherSession.get(Employee.class, 1L);
print("另一个事务",statistics);

otherSession.get(Employee.class, 2L);
print("查询另一条记录",statistics);

//提交第二个事务
otherTransaction.commit();
otherSession.close();

最终结果为:

执行结果

  • 第一次get的时候缓存中没有,那么从数据库中读一条记录;
  • 第二次get的时候是从一级中读取(不读数据库);
  • 当调用evict释放查出的数据后在get会从二级缓存中查(不读数据库);
  • 当开启另一个事务再get时依旧是从二级缓存中读取(二级缓存跨事务);
  • 当查询另一条记录时一、二级缓存中都没有,所以直接在数据库中查询。

Region Factory

Hibernate的二级缓存是对真实的缓存提供者(cache provider)透明的,org.hibernate.cache.spi.RegionFactory接口囊括了缓存提供者应该实现的各种细节,该接口其实是Hibernate和缓存提供者的一个桥梁。比较知名的缓存提供者有:

  • Ehcache : 成熟、使用范围广,本地缓存,单节点
  • Hazelcast :分布式、多节点
  • Infinispan : 分布式、多节点
  • Hibernate Redis : Redis实现的Hibernate二级缓存框架

Hibernate二级缓存策略

  • 只读(READ_ONLY): 当实体数据从来不会改变的时候使用(当尝试更新该实体对象时会抛出异常),该策略比较简单,但性能最佳,适用于那些不需要改动的静态数据。

  • 不严格读写(ONSTRICT_READ_WRITE): 当事务提交后受影响的数据发生变化后缓存才会被更新,因此策略无法保证强一致性,存在一个短暂的窗口期,在这个期间可能从缓存中获取的数据已经是脏数据了。如果应用知识偶尔需要更新数据,并且不需要严格的事务隔离,可以考虑使用该种策略。缓存实例没有被加锁,如果两个事务同时修改数据,我们不知道我们到底拿到了什么数据,这种策略不能保证缓存中的数据和数据库中的数据一致。最好设置缓存的有效期。

  • 读写(READ_WRITE): 这种策略通过加锁保持了数据的强一致性,当一个被缓存的实体数据发生变化,锁将会被添加到缓存中,直到事务被提交才释放该锁,此时当事务访问加锁的实体数据时将会直接访问数据库。

  • 事务(TRANSACTIONAL): 这种策略应用于JTA环境中

缓存管理

如果没有设置缓存过期(expiration)和缓存释放(eviction)策略,缓存将会一致增加直到耗尽所有内存。通常情况下Hibernate将缓存管理的职责转交给缓存提供者(Cache Provider)。

集合的缓存

默认情况下集合是不会被缓存的,只有明确地标注为可缓存,该集合对象才能被缓存。

缓存状态内部的内部表征

实体并没有缓存实体的对象实例到二级缓存中,而是拆散(disassembled)的状态,返回缓存的时候需要重新组装(assembly)。

  • 二级缓存中没有保存ID(主键),主键被以cache key的身份保存。
  • 被@Transient修饰的属性不会被缓存
  • 集合没有被缓存
  • 没有关联关系的属性会被以他原来的形式缓存
  • 在多对一、 一对一的关联关系的只有外键会缓存

这种设计只是反映了潜在的对应关系,这样提高了空间利用率,同时也方便关联的实体类的协调。

集合缓存的内部表征

在一对多,多对多的集合需要显式地指出才会被缓存。其实Hibernate将每一个集合的缓存放在其他的缓存域中,而不是该实体对象所在的缓存域中,集合缓存域的名称由类名+集合的属性名组成,通过域的名字可以查看到缓存的数据。

还有需要指出的是,只有集合中各个对象的主键会被缓存,然后通过这些主键再去查找对象的其他属性,因此最好把集合中的实体类也设置成可被缓存。

查询缓存

可以HQL 的查询结果也缓存起来,当要经常查询很少发生变大的数据集时做查询缓存是很有用的。要启动查询缓存只要设置hibernate.cache.use_query_cache属性为true即可。

查询缓存的最佳实践

  • 进行查询缓存的时候只有实体的主键会被缓存,所以强烈推荐使用二级缓存。

  • Hibernate会为每一个查询参数设置一个缓存,因此如果一个查询有很多的查询参数组合,那么这个查询不适合做查询缓存。

  • 如果实体对应的数据库的数据会经常发生变动,那么该查询不适合做查询缓存,因为当数据库中的数据发生变化,缓存将会无效,不管已经发生变化的实例是否已经缓存。

  • 所有的查询缓存被缓存在rg.hibernate.cache.internal.StandardQueryCache缓存域下。当实体或者集合将要被缓存的时候,可以根据需要自定义缓存参数。可以为每个查询缓存设置域名称,以便为不同的查询缓存提供不同的设置。

  • Hibernate在域org.hibernate.cache.spi.UpdateTimestampsCache保存着数据库的最后一个更新的时间戳。当在使用查询缓存的时候这个作用域非常重要,因为Hibernate通过这个来判断缓存的实体是否已经过期。在该缓存域中的属于不应该被释放或者过期处理,因为这里的缓存的数据是为了和在表中数据查询做协调的。最好为这个缓存域设置关闭自动释放和过期处理,因为它也消耗不了太多空间。

© 著作权归作者所有

architect刘源源

architect刘源源

粉丝 169
博文 564
码字总数 941209
作品 0
浦东
程序员
私信 提问
Hibernate查询缓存全面分析

这里介绍Hibernate查询缓存对Iterator不起作用,只对List起作用。下面我们这种介绍把二级缓存 和 Hibernate查询缓存 结合使用。 AD: 本文向大家介绍Hibernate查询缓存,可能好多人还不了解H...

dong.li
2012/04/24
319
0
hibernate 缓存机制

缓存:缓存是什么,解决什么问题? 位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为缓存Cache。缓存目的:让数据更接近于应用程序,协调速度不匹配...

世界和平维护者
2016/08/09
70
0
Hibernate与 MyBatis的比较

最近做了一个Hibernate与MyBatis的对比总结,希望大家指出不对之处。 第一章 Hibernate与MyBatis Hibernate 是当前最流行的O/R mapping框架,它出身于sf.net,现在已经成为Jboss的一部分。 ...

jiyayun
2012/11/15
1K
2
MyBatis和Hibernate相比,优势在哪里?

1、开发对比开发速度 hibernate的真正掌握要比Mybatis来得难些。Mybatis框架相对简单很容易上手,但也相对简陋些。个人觉得要用好Mybatis还是首先要先理解好Hibernate。 开发社区 Hibernate ...

qq5923dd411b8fa
2018/06/26
0
0
MyBatis和Hibernate相比,优势在哪里?

1、开发对比开发速度 hibernate的真正掌握要比Mybatis来得难些。Mybatis框架相对简单很容易上手,但也相对简陋些。个人觉得要用好Mybatis还是首先要先理解好Hibernate。 开发社区 Hibernate ...

park
2017/11/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Jenkins World 贡献者峰会及专家答疑展位

本文首发于:Jenkins 中文社区 原文链接 作者:Marky Jackson 译者:shunw Jenkins World 贡献者峰会及专家答疑展位 本文为 Jenkins World 贡献者峰会活动期间的记录 Jenkins 15周岁啦!Jen...

Jenkins中文社区
20分钟前
6
0
杂谈:面向微服务的体系结构评审中需要问的三个问题

面向微服务的体系结构如今风靡全球。这是因为更快的部署节奏和更低的成本是面向微服务的体系结构的基本承诺。 然而,对于大多数试水的公司来说,开发活动更多的是将现有的单块应用程序转换为...

liululee
34分钟前
6
0
OSChina 周二乱弹 —— 我等饭呢,你是不是来错食堂了?

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 自行车丢了:给主编推荐首歌 《クリスマスの夜》- 岡村孝子 手机党少年们想听歌,请使劲儿戳(这里) @烽火燎原 :国庆快来,我需要长假! ...

小小编辑
今天
329
8
玩转 Springboot 2 之热部署(DevTools)

Devtools 介绍 SpringBoot 提供了热部署的功能,那啥是热部署累?SpringBoot官方是这样说的:只要类路径上的文件发生更改,就会自动重新启动应用程序。在IDE中工作时,这可能是一个有用的功能...

桌前明月
今天
5
0
CSS--列表

一、列表标识项 list-style-type none:去掉标识项 disc:默认实心圆 circle:空心圆 squire:矩形 二、列表项图片 list-style-img: 取值:url(路径) 三、列表项位置 list-style-position:...

wytao1995
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部