第6章 二级缓存
Hibernate维护了两个级别的缓存,一个是线程级别的一级缓存,一个是进程级别的二级缓存。其中一级缓存是由Session对象维护的,二级缓存是由SessionFactory维护的。
在Web应用中Servlet容器也就是服务器的运行对应一个大的进程,而具体每一个请求的处理则是由线程执行的。所以线程级别的一级缓存只能在当前请求处理过程中可用,线程结束就释放了,存在时间很短;而二级缓存工作在进程级别所以只要服务器还在运行就一直有效。
①内置缓存:Hibernate自带的,不可卸载。通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到 SessionFactory的缓存中。映射元数据是映射文件中数据(.hbm.xml文件中的数据)的复制。该内置缓存是只读的。
②外置缓存(二级缓存):由可配置的缓存插件维护。在默认情况下,SessionFactory不会启用这个缓存插件。外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。
二级缓存可以设定以下4种类型的并发访问策略,每一种访问策略对应一种事务隔离级别
①非严格读写(Nonstrict-read-write):不保证缓存与数据库中数据的一致性。提供Read Uncommited事务隔离级别,对于极少被修改,而且允许脏读的数据,可以采用这种策略
②读写型(Read-write):提供Read Commited数据隔离级别。对于经常读但是很少被修改且不允许脏读的数据,可以采用这种隔离类型。
③事务型(Transactional):仅在受管理环境下适用。它提供了Repeatable Read事务隔离级别,可以防止脏读和不可重复读。
④只读型(Read-Only):提供Serializable数据隔离级别,对于从来不会被修改的数据,可以采用这种访问策略
并发访问策略 |
隔离级别 |
Nonstrict-read-write |
Read Uncommited |
Read-write |
Read Commited |
Transactional |
Repeatable Read |
Read-Only |
Serializable |
①Hibernate的二级缓存是进程或集群范围内的缓存
②二级缓存是由可配置的的插件维护的。Hibernate允许选用以下类型的缓存插件:
[1]EHCache:可作为进程范围内的缓存,存放数据的物理介质可以使内存或硬盘,对Hibernate的查询缓存提供了支持
[2]OpenSymphony OSCache:可作为进程范围内的缓存,存放数据的物理介质可以使用内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持
[3]SwarmCache:可作为集群范围内的缓存,但不支持Hibernate的查询缓存
[4]JBossCache:可作为集群范围内的缓存,支持Hibernate的查询缓存
③4种缓存插件支持的并发访问策略(x 代表支持, 空白代表不支持)
二级缓存插件 |
Nonstrict-read-write |
Read-write |
Transactional |
Read-Only |
EHCache |
√ |
√ |
|
√ |
OpenSymphony OSCache |
√ |
√ |
|
√ |
SwarmCache |
√ |
|
|
√ |
JBossCache |
|
|
√ |
√ |
①导入EHCache插件的JAR包
hibernate-release-4.2.4.Final\lib\optional\ehcache目录下的所有JAR包
②加入配置文件
hibernate-release-4.2.4.Final\project\etc\ehcache.xml
③在hibernate.cfg.xml配置文件中启用二级缓存
<property name="cache.use_second_level_cache">trueproperty>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactoryproperty>
<mapping resource="com/atguigu/mapping/bean/Department.hbm.xml"/> <mapping resource="com/atguigu/mapping/bean/Employee.hbm.xml"/>
<class-cache usage="read-write" class="com.atguigu.mapping.bean.Employee"/> |
④也可以在hbm文件中配置
<class name="Employee" table="EMPS">
<cache usage="read-write"/> |
①类级别的二级缓存对集合无效
@Test public void test03() { //尽管配置了类级别的二级缓存,但对于集合而言仍然 不起作用 Department department = (Department) session.get(Department. class, 1); System.out.println(department.getDeptName()); System.out.println(department.getEmpSet().size());
transaction.commit(); session.close();
session = factory.openSession(); transaction = session.beginTransaction();
depart ment = (Department) session.get(Department. class, 1); System. out.println(department.getDeptName()); System. out.println(department.getEmpSet().size()); } |
②配置集合对象使用二级缓存
<class-cache usage="read-write" class="com.atguigu.mapping.bean.Department"/>
<collection-cache usage="read-write" collection="com.atguigu.mapping.bean.Department.empSet"/> |
③注意
如果配置集合对象的二级缓存,但没有配置集合元素的二级缓存,则对集合对象中保存的就仅仅是元素的OID。在需要使用集合对象时,Hibernate会根据每一个OID再发送SQL语句查询具体的元素对象,导致额外多出很多SQL语句。
④也可以在hbm文件中配置,集合对象在set标签内配置。
<set name="empSet" table="EMPS" inverse="true" lazy="true">
<cache usage="read-write"/> <key> <column name="dept_id_fk"/> key> <one-to-many class="Employee"/> set> |
maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> maxElementsInMemory="1" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" /> |
①默认情况下执行HQL查询语句时,二级缓存不起作用
@Test public void testCache01 () { List System. out.println(list.size()); list = session.createQuery("From Employee e").list(); System. out.println(list.size()); } |
②需要开启查询缓存
@Test public void testCache02() { Query query = session.createQuery("From Employee e");
//开启查询缓存 query.setCacheable( true); List System.out.println(list.size());
list = query.list(); System.out.println(list.size()); } |
<property name="cache.use_query_cache">trueproperty> |
时间戳缓存区域存放了对于查询结果相关的表进行插入、更新或删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期。其运行过程如下:
①T1时刻执行查询操作,把查询结果存放在QueryCache区域,记录该区域的时间戳为T1
②T2时刻对查询结果相关的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域
③T3时刻执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳,若T2>T1,那么就丢弃原先存放在QueryCache区域的查询结果,重新到数据库中查询数据,再把结果存放到QueryCache区域;若T2
①和list()方法很相似
②list()方法执行的SQL语句包含实体类对应的数据表的所有字段
③iterate()方法执行的SQL语句中仅包含OID字段
④当遍历访问iterate()方法的结果集时
[1]得到的是集合中的每个OID的值
[2]根据OID的值到Session一级缓存和二级缓存中查询,看是否存在OID对应的实体类对象
[3]如果存在则直接返回该对象
[4]如果不存在则根据OID发送SQL语句查询该对象
⑤大多数场合下,应考虑使用list()方法
⑥iterate()方法仅在特定场合下能够稍微提高一点查询效率
[1]要查询的数据库表中包含大量字段,这时只查询OID就会比较快捷
[2]启用了二级缓存,且二级缓存中已经包含了待查询的对象