Hibernate学习笔记 | 解析二级缓存
缓存
计算机领域非常通用的概念,它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能,缓存中的数据是数据存储源中数据的拷贝。
缓存的物理介质通常是内存。
Hibernate提供的两种级别的缓存:
- 第一级别的缓存:Session级别的缓存
它是属于事务范围的缓存,这一级别的缓存是由Hibernate管理的。 - 第二级别的缓存:SessionFactory级别的缓存
它是属于进程范围的缓存。
SessionFactory级别的缓存
SessionFactory级别的缓存可以分为两类
内置缓存
Hibernate自带的,不可卸载的,通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中的数据(.hbm.xml文件中的数据)的复制,该内置缓存是只读的。外置缓存(二级缓存)
一个可配置的缓存插件。在默认情况下,SessionFactory不会启用这个缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。
Hibernate的二级缓存
适合放入二级缓存中的数据:
- 很少被修改
- 不是很重要的数据,允许出现偶尔的并发问题
不适合放入二级缓存中的数据:
- 经常被修改
- 财务数据,绝对不允许出现并发问题
- 与其他应用程序共享的数据
二级缓存的并发访问策略
两个并发的事务同时访问持久层的缓存的相同数据时,也有可能出现各类并发问题。
二级缓存可以设定以下4种类型的并发访问策略,每一种访问策略对应一种事务隔离级别
- 非严格读写(Nostrict-read-write)
不保证缓存与数据库中数据的一致性,提供Read Uncommited事务隔离级别,对于极少被修改,而且允许脏读的数据,可以采用这种策略。 - 读写型(Read-write)
提供Read Commited数据隔离级别,对于经常读但是很少被修改的数据,可以采用这种隔离类型。因为它可以防止脏读。 - 事务型(Transactional)
仅在受管理环境下适用,它提供了Repeatable Read事务隔离级别,对于经常读但是很少被修改的数据,揭阳采用这种隔离类型,因为它可以防止脏读和不可重复读。 - 只读型(Read-Only)
提供了Serializable数据隔离级别,对于从来不会被修改的数据,可以采用这种访问策略。
配置进程范围内的二级缓存的步骤
- 加入二级缓存插件的jar包及配置文件
jar包有:ehcache-2.10.6.jar
,hibernate-ehcache-5.4.3.Final.jar
,slf4j-api-1.7.25.jar
。
配置文件ehcache.xml如下:
<ehcache><diskStore path="java.io.tmpdir"/><defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="true"/><cache name="sampleCache1"maxElementsInMemory="10000"eternal="false"timeToIdleSeconds="300"timeToLiveSeconds="600"overflowToDisk="true"/><cache name="sampleCache2"maxElementsInMemory="1000"eternal="true"timeToIdleSeconds="0"timeToLiveSeconds="0"overflowToDisk="false"/>
</ehcache>
- 配置Hibernate.cfg.cml文件
配置启用hibernate的二级缓存:<property name="cache.use_second_level_cache">true</property>
配置Hibernate二级缓存使用的产品:<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property>
配置对哪些类使用Hibernate的二级缓存:<class-cache class="com.cerr.hibernate.entities.Employee" usage="read-write" />
实际上也可以在.hbm.xml
文件中配置对哪些类使用二级缓存及二级缓存的策略。例如在Employee.hbm.xml
文件的class
节点里面加上<cache usage="read-write"/>
。
设置二级缓存之后,测试如下:
@org.junit.Testpublic void testHibernateSecondLevelCache(){Employee employee = session.get(Employee.class,1);System.out.println(employee.getName());//关闭session后重新连接transaction.commit();session.close();session = sessionFactory.openSession();transaction = session.beginTransaction();Employee employee1 = session.get(Employee.class,1);System.out.println(employee1.getName());}
在没设置二级缓存的时候,该情形会发送两次sql语句,因为在中间关闭了session
后再重新开启。而我们已经设置了二级缓存,因此只会发送一条sql语句。
集合级别的二级缓存的配置
- 配置对集合使用二级缓存,例如:
<collection-cache collection="com.cerr.hibernate.entities.Department.emps" usage="read-write" />
,也可以在hbm.xml
文件中使用<cache>
标签进行配置。 - 还需要配置集合中的元素对应的持久化类也使用二级缓存,否则将会多出n条SQL语句。
以Department为例,配置文件hibernate.cfg.xml
如下:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration><session-factory><!-- 配置连接数据库的基本信息 --><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost:3306/hibernate5</property><property name="connection.username">root</property><property name="connection.password">root</property><!-- 配置hibernate的基本信息--><!-- hibernate所使用的的数据库方言 --><property name="dialect">org.hibernate.dialect.MySQL5Dialect</property><!-- 执行操作时是否在控制台打印SQL--><property name="show_sql">true</property><!-- 是否对SQL进行格式化--><property name="format_sql">true</property><!-- 指定生成数据表的策略--><property name="hibernate.hbm2ddl.auto">update</property><!-- 设置hibernate的事务隔离级别 --><property name="connection.isolation">2</property><property name="use_identifier_rollback">true</property><!-- 配置C3P0数据源 --><property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property><property name="c3p0.max_size">10</property><property name="c3p0.min_size">5</property><property name="c3p0.acquire_increment">2</property><property name="c3p0.idle_test_period">2000</property><property name="c3p0.timeout">2000</property><property name="c3p0.max_statements">10</property><!-- 设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数--><property name="hibernate.jdbc.fetch_size">100</property><!-- 设定对数据库进行批量删除,更新,插入的时候批次的大小 --><property name="hibernate.jdbc.batch_size">30</property><!-- 启用二级缓存--><property name="cache.use_second_level_cache">true</property><!-- 配置使用的二级缓存的产品 --><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property><mapping resource="com/cerr/hibernate/entities/Employee.hbm.xml"/><mapping resource="com/cerr/hibernate/entities/Department.hbm.xml"/><class-cache class="com.cerr.hibernate.entities.Employee" usage="read-write" /><class-cache class="com.cerr.hibernate.entities.Department" usage="read-write" /><!-- 对Department的集合使用二级缓存--><collection-cache collection="com.cerr.hibernate.entities.Department.emps" usage="read-write" /></session-factory>
</hibernate-configuration>
测试:
@org.junit.Testpublic void testCollectionSecondLevelCache(){Department department = session.get(Department.class,1);System.out.println(department.getName());System.out.println(department.getEmps().size());transaction.commit();session.close();session = sessionFactory.openSession();transaction = session.beginTransaction();Department department1 = session.get(Department.class,1);System.out.println(department1.getName());System.out.println(department1.getEmps().size());}
对ehcache.xml文件的解析
<ehcache><!-- 指定一个目录:当EHCache把数据写到硬盘上时,将把数据写到这个目录下 --><diskStore path="java.io.tmpdir"/><!-- 设置缓存的默认数据过期策略 --><defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="true"/><!-- 设置具体的命名缓存的数据过期策略,每个命名缓存代表一个缓存区域--><cache name="com.cerr.hibernate.entities.Employee"maxElementsInMemory="10000"eternal="false"timeToIdleSeconds="300"timeToLiveSeconds="600"overflowToDisk="true"/><cache name="com.cerr.hibernate.entities.Department"maxElementsInMemory="1000"eternal="true"timeToIdleSeconds="0"timeToLiveSeconds="0"overflowToDisk="false"/>
</ehcache>
关于几个标签
<diskStore>
标签
指定一个目录:当EHCache把数据写到硬盘上时,将把数据写到这个目录下<defaultCache>
标签
设置缓存的默认数据过期策略<cache>
标签
设置具体的命名缓存的数据过期策略,每个命名缓存代表一个缓存区域
Hibernate在不同的缓存区域保存不同的类/集合
对于类而言,区域的名称是类名,例如:com.cerr.domain.Customer
对于集合而言,区域的名称是类名加属性名,例如:com.cerr.domain.Customer.orders
cache元素的属性
name
:设置缓存的名字,它的取值为类的全限定名或类的集合的名字。maxInMemory
:设置基于内存的缓存中可存放的对象的最大数目。etemal
:设置对象是否为永久的,true
表示永不过期,此时将忽略timeToldleSeconds
和timeToLiveSeconds
属性:默认是false
。timeToldleSeconds
:设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。timeToLiveSeconds
:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中,该属性值必须大于或等于timeToldleSeconds
属性值。overflowToDisk
:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写在基于硬盘的缓存中。
查询缓存
默认情况下,设置的缓存对HQL及QBC查询是无效的,但可以通过设置查询缓存来使其支持这两种查询。
设置的步骤:
- 在
hibernate.cfg.xml
文件中声明开启查询缓存,例如:<property name="cache.use_query_cache">true</property>
- 调用
Query
或Criteria
的setCacheable(true)
。
注意:查询缓存依赖于二级缓存,即若要使用查询缓存,必须先配置二级缓存,否则无法使用。
Demo如下:
@org.junit.Testpublic void testQueryCache(){Query query = session.createQuery("FROM Employee ");//设置查询缓存query.setCacheable(true);List<Employee> employees = query.list();System.out.println(employees.size());employees = query.list();System.out.println(employees.size());}
时间戳缓存区域
时间戳缓存区域存放了对于查询结果相关的表进行插入,更新或删除操作的时间戳,Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期,其运行过程如下:
- T1时刻执行查询操作,把查询结果存放在QueryCache区域,记录该区域的时间戳为T1.
- T2时刻对查询结果相关的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域。
- T3时刻执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳,若T2>T1,那么就丢弃原先存放在QueryCache区域的查询结果,重新到数据库中查询数据,再把结果存放到QueryCache区域;若T2<T1,直接从QueryCache中获得查询结果。
Query接口的iterate()方法
同list()
一样也能执行查询操作。
list()
执行的SQL语句包含实体类对应的数据表的所有字段。
iterator()
执行的SQL语句中仅包含实体类对应的数据表的ID字段。
当遍历访问结果集时,该方法先到Session缓存及二级缓存中查看是否存在特定OID的对象,如果存在,就直接返回该对象,如果不存在该对象就通过相应的SQL Select语句到数据库中加载特定的实体对象
大多数情况下,应考虑使用list()
执行查询操作,iterator()
仅仅在满足以下条件的场合,可以稍微提高查询性能:
- 要查询的数据表中包含大量字段
- 启用了二级缓存,且二级缓存中可能已经包含了待查询的对象。
Hibernate学习笔记 | 解析二级缓存相关推荐
- hibernate学习笔记(总结)
hibernate学习笔记 课程内容 6 1 HelloWorld 6 2 Hibernate原理模拟-什么是O/R Mapping以及为什么要有O/R Mapping 6 3 常见的0/R框架(了解 ...
- 马士兵Hibernate学习笔记
马士兵hibernate学习笔记 课程内容 6课程内容 1 HelloWorld 6 2 Hibernate原理模拟-什么是O/R Mapping以及为什么要有O/R Mapping 6 3 常见的0 ...
- Hibernate学习笔记Session.evict(user)方法
@[TOC]Hibernate学习笔记Session.evict(user)方法 Hibernate学习笔记Session.evict(user)方法 首先我们要明白Session.flush(use ...
- hibernate学习笔记二
上一篇关于hibernate学习笔记一,主要是作为hibernate的入门知识.没有和spring发生任何关系,这一篇我将把spring集成进去,看spring如何管理hibernate,还有和未使用 ...
- Hibernate学习笔记(一)
####1.1Hibernate框架的学习路线 第一天:Hibernate的入门(Hibernate的环境搭建.Hibernate的API.Hibernate的CRUD) 第二天:Hibernat ...
- Hibernate学习笔记(一)----针对不同的数据库不同的配置
Hibernate初学笔记 l Hibernate初步配置: 1 新建项目 2 学习建立user-library-hibernate,并加入相应的jar包(hibernate核心jar包,lib下的所 ...
- Hibernate+Spring整合使用二级缓存
1.选择缓存的实现 我们一般选择轻量级的EHCache,在hibernate.cft.xml里面的<session-factory>里面配置如下 <!--选择缓存实现--> & ...
- mybatis和hibernate的一级、二级缓存
MyBatis一级缓存: hibernate一级缓存: 基本差不多 HashMap本地缓存,作用域为session,session级别的缓存,通过get,update可以将对象放到一级缓存中,当 Se ...
- Hibernate学习笔记之EHCache的配置
Hibernate默认二级缓存是不启动的,启动二级缓存(以EHCache为例)需要以下步骤: 1.添加相关的包: Ehcache.jar和commons-logging.jar,如果hibernate ...
最新文章
- OpenResty简介
- CMD 命令行查看端口被哪个程序占用,并根据PID值,找到相应的程序,关闭掉对应服务或进程!...
- KinhDown_v2.4.42稳定版 百度云最新不限速下载工具
- Python gevent学习笔记 1
- IOT(33)---NB-IOT通用物联解决方案
- 阿里影业正式成为阿里集团子公司 俞永福辞任执行董事
- 今天终于安装了Snippet Compiler!!!
- html 制作静态页面新知识
- 数据集:不同地区居民消费数据
- android 下载服务器的txt文档
- EasyPlayer播放H.265的HLS视频流出现加载异常的问题分析及解决方法
- 安科瑞ACX电瓶车智能充电桩,支付方式可选择刷卡、扫码、免费充电使用,设备内部可引出10路出线至专用插座
- 雷神加速器无限更新失败️️️
- 利用Axure做原型设计
- 不藏了,我的一千行 MySQL 学习笔记(2万字长文)
- 屏幕分辨率(QQVGA、QVGA、VGA、XGA、WXGA、WUXGA和WSXGA+)
- 掌握c语言编程是什么程度,学习嵌入式C语言要掌握到什么程度?
- 【leetcode】537. Complex Number Multiplication(Python C++)
- 纠错技术之FEC(向前纠错)
- 初学JSP,运行一个JSP小程序