06_Hibernate-----二级缓存

6 二级缓存

 

  1. Hibernate缓存

Hibernate维护了两个级别的缓存,一个是线程级别一级缓存,一个是进程级别二级缓存。其中一级缓存是由Session对象维护的,二级缓存是由SessionFactory维护的。

  1. Web应用中的进程和线程

在Web应用中Servlet容器也就是服务器的运行对应一个大的进程,而具体每一个请求的处理则是由线程执行的。所以线程级别的一级缓存只能在当前请求处理过程中可用,线程结束就释放了,存在时间很短;而二级缓存工作在进程级别所以只要服务器还在运行就一直有效。

  1. Hibernate二级缓存概述
    1. SessionFactory的缓存可以分为两类

①内置缓存:Hibernate自带的,不可卸载。通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到 SessionFactory的缓存中。映射元数据是映射文件中数据(.hbm.xml文件中的数据)的复制。该内置缓存是只读的。

②外置缓存(二级缓存):由可配置的缓存插件维护。在默认情况下,SessionFactory不会启用这个缓存插件。外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存硬盘

    1. 数据要求
  1. 适合存入二级缓存的数据
    • 很少被修改,经常被查询
    • 不重要,允许出现偶尔的并发问题
  2. 不适合存入二级缓存的数据
    • 经常被修改的数据:会由于更新不及时导致二级缓存中的数据总是错误的
    • 财务数据,不允许任何的并发问题
    • 与其他应用程序共享的数据
    1. Hibernate二级缓存架构

 

    1. 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

 

  1. 管理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

 

 

 

  1. 配置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"/>

 

  1. 集合级别的二级缓存

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

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

    <!-- hbm 文件中配置集合对象使用二级缓存 -->

    <cache usage="read-write"/>

    <key>

        <column name="dept_id_fk"/>

    </key>

    <one-to-many class="Employee"/>

</set>

 

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

    />

 

  1. 查询缓存

①默认情况下执行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>

 

  1. 更新时间戳缓存

时间戳缓存区域存放了对于查询结果相关的表进行插入、更新或删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期。其运行过程如下:

①T1时刻执行查询操作,把查询结果存放在QueryCache区域,记录该区域的时间戳为T1

②T2时刻对查询结果相关的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域

③T3时刻执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳,若T2>T1,那么就丢弃原先存放在QueryCache区域的查询结果,重新到数据库中查询数据,再把结果存放到QueryCache区域;若T2<T1,直接从QueryCache中获得查询结果

 

  1. Query接口的iterate()方法

①和list()方法很相似

②list()方法执行的SQL语句包含实体类对应的数据表的所有字段

③iterate()方法执行的SQL语句中仅包含OID字段

④当遍历访问iterate()方法的结果集时

[1]得到的是集合中的每个OID的值

[2]根据OID的值到Session一级缓存和二级缓存中查询,看是否存在OID对应的实体类对象

[3]如果存在则直接返回该对象

[4]如果不存在则根据OID发送SQL语句查询该对象

⑤大多数场合下,应考虑使用list()方法

⑥iterate()方法仅在特定场合下能够稍微提高一点查询效率

[1]要查询的数据库表中包含大量字段,这时只查询OID就会比较快捷

[2]启用了二级缓存,且二级缓存中已经包含了待查询的对象

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值