二级缓存

1 Hibernate缓存
Hibernate维护了两个级别的缓存,一个是线程级别的一级缓存,一个是进程级别的二级缓存。其中一级缓存是由Session对象维护的,二级缓存是由SessionFactory维护的。
这里写图片描述
2 Web应用中的进程和线程
在Web应用中Servlet容器也就是服务器的运行对应一个大的进程,而具体每一个请求的处理则是由线程执行的。所以线程级别的一级缓存只能在当前请求处理过程中可用,线程结束就释放了,存在时间很短;而二级缓存工作在进程级别所以只要服务器还在运行就一直有效。
这里写图片描述
3 Hibernate二级缓存概述
3.1 SessionFactory的缓存可以分为两类
①内置缓存:Hibernate自带的,不可卸载。通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到 SessionFactory的缓存中。映射元数据是映射文件中数据(.hbm.xml文件中的数据)的复制。该内置缓存是只读的。
②外置缓存(二级缓存):由可配置的缓存插件维护。在默认情况下,SessionFactory不会启用这个缓存插件。外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。
3.2 数据要求
 适合存入二级缓存的数据
 很少被修改,经常被查询
 不重要,允许出现偶尔的并发问题
 不适合存入二级缓存的数据
 经常被修改的数据:会由于更新不及时导致二级缓存中的数据总是错误的
 财务数据,不允许任何的并发问题
 与其他应用程序共享的数据
3.3 Hibernate二级缓存架构
这里写图片描述

3.4 Hibernate二级缓存的并发访问策略
二级缓存可以设定以下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

4 管理Hibernate的二级缓存
①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 √ √

5 配置Hibernate二级缓存
①导入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">true</property >

<!-- 配置使用的二级缓存产品 -->
<!--
参考 hibernate-release-4.2.4.Final\project\etc\hibernate.properties文件看到
hibernate.cache.region.factory_class org.hibernate.cache.internal.EhCacheRegionFactory
但这个值不对,应该是:org.hibernate.cache.ehcache.EhCacheRegionFactory
-->
<property name= "hibernate.cache.region.factory_class" >org.hibernate.cache.ehcache.EhCacheRegionFactory </property >

<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">

<!-- 在hbm映射文件中配置二级缓存策略 -->
<cache usage ="read-write"/>

6 集合级别的二级缓存
①类级别的二级缓存对集合无效

    @Test
     public void test03() {
            //尽管配置了类级别的二级缓存,但对于集合而言仍然 不起作用
           De partment 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">
    <!-- 在hbm 文件中配置集合对象使用二级缓存 -->
    <cache usage ="read-write"/>
    <key >
        <column name ="dept_id_fk" />
    </key >
    <one-to-many class ="Employee" />
</set >

7 EHCache自身配置文件

    <!-- 
         指定一个目录:当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.
    -->    
    <diskStore path="d:\\tempDirectory"/>

    <!-- 
         设置缓存的默认数据过期策略
    -->   
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

        <!-- 
             设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
             缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
             如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>
             Hibernate 在不同的缓存区域保存不同的类/集合。
               对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
               对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders
        -->
        <!-- 
             name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字
          maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目

          eternal:设置对象是否为永久的,true表示永不过期
          此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性;默认值是false

          timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。
          当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。

          timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
          如果此值为0,表示对象可以无限期地存在于缓存中。该属性值必须大于或等于timeToIdleSeconds属性值

          overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
        -->
    <cache name="com.atguigu.hibernate.entities.Employee"
        maxElementsInMemory="1"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <cache name="com.atguigu.hibernate.entities.Department.emps"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />

8 查询缓存
①默认情况下执行HQL查询语句时,二级缓存不起作用

@Test
     public void testCache01 () {
           List<Employee> list = session.createQuery("From Employee e").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<Employee> list = query.list();
           System. out.println(list.size());

           list = query.list();
           System. out.println(list.size());
     }

        <!-- 配置启用查询缓存 -->
        <property name ="cache.use_query_cache"> true</ property>

9 更新时间戳缓存
时间戳缓存区域存放了对于查询结果相关的表进行插入、更新或删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期。其运行过程如下:
①T1时刻执行查询操作,把查询结果存放在QueryCache区域,记录该区域的时间戳为T1
②T2时刻对查询结果相关的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域
③T3时刻执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳,若T2>T1,那么就丢弃原先存放在QueryCache区域的查询结果,重新到数据库中查询数据,再把结果存放到QueryCache区域;若T2<T1,直接从QueryCache中获得查询结果

10 Query接口的iterate()方法
①和list()方法很相似
②list()方法执行的SQL语句包含实体类对应的数据表的所有字段
③iterate()方法执行的SQL语句中仅包含OID字段
④当遍历访问iterate()方法的结果集时
[1]得到的是集合中的每个OID的值
[2]根据OID的值到Session一级缓存和二级缓存中查询,看是否存在OID对应的实体类对象
[3]如果存在则直接返回该对象
[4]如果不存在则根据OID发送SQL语句查询该对象
⑤大多数场合下,应考虑使用list()方法
⑥iterate()方法仅在特定场合下能够稍微提高一点查询效率
[1]要查询的数据库表中包含大量字段,这时只查询OID就会比较快捷
[2]启用了二级缓存,且二级缓存中已经包含了待查询的对象

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值