Hibernate学习笔记 | 解析二级缓存

2019-09-29  本文已影响0人  一颗白菜_

缓存

计算机领域非常通用的概念,它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能,缓存中的数据是数据存储源中数据的拷贝。
缓存的物理介质通常是内存。

Hibernate提供的两种级别的缓存:

SessionFactory级别的缓存

SessionFactory级别的缓存可以分为两类


Hibernate的二级缓存

适合放入二级缓存中的数据:

不适合放入二级缓存中的数据:

二级缓存的并发访问策略

两个并发的事务同时访问持久层的缓存的相同数据时,也有可能出现各类并发问题。

二级缓存可以设定以下4种类型的并发访问策略,每一种访问策略对应一种事务隔离级别

配置进程范围内的二级缓存的步骤

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

实际上也可以在.hbm.xml文件中配置对哪些类使用二级缓存及二级缓存的策略。例如在Employee.hbm.xml文件的class节点里面加上<cache usage="read-write"/>

设置二级缓存之后,测试如下:

@org.junit.Test
    public 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语句。

集合级别的二级缓存的配置

以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.Test
    public 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"/>

    <!-- 设置缓存的默认数据过期策略 -->
    <defaultCache
        maxElementsInMemory="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>

关于几个标签

Hibernate在不同的缓存区域保存不同的类/集合
对于类而言,区域的名称是类名,例如:com.cerr.domain.Customer
对于集合而言,区域的名称是类名加属性名,例如:com.cerr.domain.Customer.orders

cache元素的属性

查询缓存

默认情况下,设置的缓存对HQL及QBC查询是无效的,但可以通过设置查询缓存来使其支持这两种查询。

设置的步骤:

注意:查询缓存依赖于二级缓存,即若要使用查询缓存,必须先配置二级缓存,否则无法使用。

Demo如下:

@org.junit.Test
    public 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通过时间戳缓存区域来判断被缓存的查询结果是否过期,其运行过程如下:

Query接口的iterate()方法

list()一样也能执行查询操作。
list()执行的SQL语句包含实体类对应的数据表的所有字段。
iterator()执行的SQL语句中仅包含实体类对应的数据表的ID字段
当遍历访问结果集时,该方法先到Session缓存及二级缓存中查看是否存在特定OID的对象,如果存在,就直接返回该对象,如果不存在该对象就通过相应的SQL Select语句到数据库中加载特定的实体对象

大多数情况下,应考虑使用list()执行查询操作,iterator()仅仅在满足以下条件的场合,可以稍微提高查询性能:

上一篇 下一篇

猜你喜欢

热点阅读