Hibernate二级缓存——SessionFactory

Hibernate二级缓存简介

  在《Hibernate一级缓存——Session》中介绍了Session级别的一级缓存,例如,当如下的代码执行时,由于一级缓存的作用,只会发送一条select语句:

@Test
    public void testCache(){

        Employee employee1 = (Employee) session.get(Employee.class, 1);
        System.out.println(employee1);

        Employee employee2 = (Employee) session.get(Employee.class, 1);
        System.out.println(employee2);
    }

Hibernate二级缓存——SessionFactory_第1张图片

  现在,我们在两次加载代码之间,先关闭当前的session和事务,再重新开启一个session和事务,那么不难理解,由于开启了新的session,所以会打印两条select语句:

@Test
    public void testCache(){

        Employee employee1 = (Employee) session.get(Employee.class, 1);
        System.out.println(employee1);

        //提交事务,关session
        transaction.commit();
        session.close();

        //开启一个新的session和事务
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Employee employee2 = (Employee) session.get(Employee.class, 1);
        System.out.println(employee2);
    }

Hibernate二级缓存——SessionFactory_第2张图片
  那么,有没有办法使就算session关闭,也能缓存employee对象的办法呢?这就是我们现在要介绍的,SessionFactory级别的,Hibernate二级缓存。
  
  缓存(Cache )是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝,应用程序在运行时直接读写缓存中的数据,只在某些特定时刻按照缓存中的数据来同步更新数据存储源。缓存的物理介质通常是内存。
  
  Hibernate中提供了两个级别的缓存:
  1.第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存,即缓存只能被当前事务访问,每个事务都有独自的缓存。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。这一级别的缓存是由hibernate管理的,一般情况下无须进行干预。
  2.第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围的缓存。缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。
  
  SessionFactory 的缓存可以分为两类:
  内置缓存: Hibernate自带的,不可卸载。通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中数据的复制,而预定义SQL语句是Hibernate根据映射元数据推到出来的。该内置缓存是只读的。
  外置缓存(二级缓存):一个可配置的缓存插件。在默认情况下,SessionFactory不会启用这个缓存插件。外置缓存中的数据是数据库中数据的复制,外置缓存的物理介质可以是内存,也可以是硬盘。
  
  缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘或磁盘,应用程序读写内在的速度显然比读写硬盘的速度快,如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。缓存的实现不仅需要作为物理介质的硬件,同时还需要用于管理缓存的并发访问和过期等策略的软件。因此,缓存是通过软件和硬件共同实现的。
  
  适合放入二级缓存中的数据:
  1.很少被修改;
  2.不是很重要的数据,允许出现偶尔的并发问题。
  
  不适合放入二级缓存中的数据:
  1.经常被修改;
  2.财务数据,绝对不允许出现并发问题;
  3.与其他应用程序共享的数据。
  
  二级缓存的并发访问策略:
  两个并发的事务同时访问持久层的缓存的相同数据时,也有可能出现各类并发问题。
  二级缓存可以设定以下4种类型的并发访问策略,每一种访问策略对应一种事务隔离级别。
  非严格读写(Nonstrict-read-write):不保证缓存与数据库种数据的一致性。提供read uncommitted事务隔离级别。对于极少被修改,而且允许脏读的数据,可以采用这种策略。
  读写型(Read-write):提供read committed事务隔离级别。对于经常读但是很少被修改的数据,可以采用这种隔离类型,它可以防止脏读。(通常选用的策略)
  事务型(Transactional):仅在受管理环境下适用。它提供了repeatable read的事务隔离级别。对于经常读,但是很少被修改的数据,可以采用这种隔离类型,它可以防止脏读和不可重复读。
  只读型(Read-Only):提供serializable事务隔离级别,对于从来不会修改的数据,可以采用这种访问策略,可以避免脏读,不可重复读和幻读。
  
  管理Hibernate的二级缓存:
  Hibernate的二级缓存是进程或者集群范围内的缓存。
  二级缓存是可配置的插件,Hibernate允许选用以下类型的缓存插件:
  EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
  OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
  SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。   JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。
  
Hibernate二级缓存——SessionFactory_第3张图片

使用Hibernate二级缓存的步骤

  
一、加入二级缓存的jar包及配置文件
  
1.加入jar包
  Hibernate二级缓存——SessionFactory_第4张图片
  
2.添加配置文件ehcache.xml到src目录下

<ehcache>

    
    <diskStore path="java.io.tmpdir"/>


    
    <defaultCache
        maxElementsInMemory="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.xml
 
1.配置启用hibernate的二级缓存

<property name="cache.use_second_level_cache">trueproperty>

2.配置hibernate二级缓存使用的产品

<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactoryproperty>

3.配置对哪些类(或属性)使用 hibernate 的二级缓存

第一种情况,类级别的二级缓存:(配置对Employee类使用二级缓存)

<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>

第二种情况,集合级别的二级缓存:(配置对Department类中的Employee集合属性emps使用二级缓存)

<collection-cache usage="read-write" collection="com.atguigu.hibernate.entities.Department.emps"/>

注意,当配置对集合属性使用二级缓存时,还需要对集合所属的类,集合中元素的类型使用二级缓存,例如,对于上述集合属性,则还需配置对Department类和Employee类使用二级缓存:(如果不对Employee类配置二级缓存,则会多出n条SQL语句,得不偿失。因为这种情况下缓存的是一个一个的employee的id,当要使用到employee对象时,需要再根据id一条一条地去数据库查询记录)

<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Department"/>
<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>

另外,配置对哪些类(或属性)使用二级缓存,还可以在映射文件中配置,例如:
类级别(在class节点下):

<cache usage="read-write"/>

集合级别:(在department映射文件的set节点下)

<cache usage="read-write"/>

  现在,测试上面的testCache方法,只会打印一条select语句:
  Hibernate二级缓存——SessionFactory_第5张图片
  对于集合的测试也是一样,只会打印两条select语句,一条用于查询department,一条用于查询employee:

@Test
    public void testCollectionSecondLevelCache(){
        Department dept = (Department) session.get(Department.class, 1);
        System.out.println(dept.getName());
        System.out.println(dept.getEmps().size()); 

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Department dept2 = (Department) session.get(Department.class, 1);
        System.out.println(dept2.getName());
        System.out.println(dept2.getEmps().size()); 
    }

Hibernate二级缓存——SessionFactory_第6张图片

二级缓存配置文件

  下面使用一个修改过的二级缓存配置文件介绍其中各个属性的作用:

<ehcache>

    
         
    <diskStore path="d:\\tempDirectory"/>


    
        
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

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

ehcache>

查询缓存

  对于经常使用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把查询结果存放在查询缓存中,以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。
  默认情况下,Hibernate设置的缓存对HQL和QBC查询是无效的,但可以通过以下步骤使其有效:
  1.配置二级缓存,因为查询缓存依赖于二级缓存.
  2.在hibernate配置文件中声明开启查询缓存。

<property name="cache.use_query_cache">trueproperty>

  3.调用Query或者Criteria的setCachable(true)。
  
例如,在没有配置查询缓存的情况下,下面的代码会打印两条select语句:

@Test
    public void testQueryCache(){
        Query query = session.createQuery("FROM Employee");

        List emps = query.list();
        System.out.println(emps.size());

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

    }

Hibernate二级缓存——SessionFactory_第7张图片
  
  进行了相关配置之后,并且在方法中设置query.setCacheable(true);则只会打印一条select语句:

@Test
    public void testQueryCache(){
        Query query = session.createQuery("FROM Employee");
        query.setCacheable(true);

        List emps = query.list();
        System.out.println(emps.size());

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

    }

Hibernate二级缓存——SessionFactory_第8张图片

  查询缓存适用于如下场合:
  1.应用程序运行时经常使用查询语句
  2.很少对与查询语句检索到的数据进行插入,删除或更新操作

时间戳缓存区域

  时间戳缓存区域存放了对于查询结果相关的表进行插入,更新或者删除操作的时间戳。Hibernate通过时间戳缓存区域来判断被缓存的查询结果是否过期,其运行过程如下:
  T1时刻执行查询操作,把结果存放在QueryCache区域,记录该区域的时间戳为T1;
  T2时刻(可能在T1之前,也可能在T1之后)对查询结果相关的表进行更新操作,Hibernate把T2时刻存放在UpdateTimestampCache区域。
  T3时刻(在T1,T2之后)执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳。若T2>T1,则丢弃原先存放在QueryCache区域的查询结果,重新到数据库中查询数据并放入QueryCache区域;若T2

@Test
    public void testUpdateTimeStampCache(){
        Query query = session.createQuery("FROM Employee");
        query.setCacheable(true);

        List emps = query.list();
        System.out.println(emps.size());

        Employee employee = (Employee) session.get(Employee.class, 1);
        employee.setSalary(30000);

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

  在第二次查询的时候,会重新用select去数据库中查找最新的记录。
  Hibernate二级缓存——SessionFactory_第9张图片

Query接口的iterate()方法

  Query的list方法返回实体类对应表的所有字段,而Query的iterate方法仅返回数据包的ID字段。当使用了iterate方法,然后遍历访问结果集时,先到Session缓存及二级缓存中查看是否存在特定OID的对象,如果存在,直接返回,否则就通过相应的SQL SELECT语句到数据库中查找特定的记录。
  在大多数情况下,应该考虑使用list方法执行查询操作,iterate方法仅在下述情况下可以稍微提高查询性能:
  1.要查询的数据表中包含大量字段;
  2.启用了二级缓存,且二级缓存中可能已经包含了待查询的对象。

你可能感兴趣的:(Hibernate,Hibernate框架学习)