Hibernate4-11 二级缓存

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

  Hibernate提供了两个级别的缓存:

  • 第一级别的缓存是Session级别的,其属于事务范围的缓存,由hibernate负责管理;
  • 第二级别的缓存是SessionFactory级别的缓存,其属于进程范围的缓存。

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

  • 内置缓存:Hibernate自带的只读缓存,不可卸载;通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中数据的复制;
  • 外置缓存(二级缓存):可配置的缓存插件;默认情况下,SessionFactory不会启用该缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。

1. Hibernate二级缓存概述

1.1 适用场合

  适合放入二级缓存中的数据:很少被修改的数据;不是很重要的数据,允许偶尔出现并发问题;
  不适合放入二级缓存的数据:经常被修改的数据;财务数据,绝对不允许出现并发问题;与其他应用程序共享的数据。

1.2 整体架构

这里写图片描述

1.3 二级缓存的并发访问策略

这里写图片描述

1.4 二级缓存的管理  

这里写图片描述

1.5 二级缓存的实现 

  第一步:加入二级缓存插件所依赖的jar包和配置文件:

  • 复制hibernate-release-4.2.4.Final\lib\optional\ehcache*.jar到当前应用的类路径下;
  • 复制hibernate-release-4.2.4.Final\project\etc\ehcache.xml到当前应用的类路径下。

  第二步:配置Hibernate配置文件:

  • 配置启用Hibernate二级缓存:
    <property name="cache.use_second_level_cache">true</property>
  • 配置Hibernate二级缓存使用的插件:
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  • 配置对哪些类使用Hibernate二级缓存:
    • 通过<class-cache/>节点配置:<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>
    • 通过映射文件的<class/>节点配置:其cache子元素表明Hibernate会缓存对象的简单属性,但不会缓存集合属性;若希望缓存集合属性中的元素,必须在set元素中加入cache子元素。

3. 查询缓存

这里写图片描述


4. 时间戳缓存

这里写图片描述


5. 了解:Query接口的Iterate()方法

这里写图片描述


附录:测试代码

HibernateTest.java:
package com.qiaobc.hibernate.hql;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class HibernateTest {

    private Session session = null;
    private Transaction transaction = null;
    private SessionFactory sessionFactory = null;

    /**
     * 1. 类级别的二级缓存
     */
    @Test
    public void testHibernateSecondLevelCache() {
        Employee emp1 = (Employee) session.get(Employee.class, 2012);
        System.out.println(emp1);

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

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

        Employee emp2 = (Employee) session.get(Employee.class, 2012);
        System.out.println(emp2);
    }

    /**
     * 2. 集合级别的二级缓存
     * 注意:还需要配置集合中的元素对应的持久化类也使用二级缓存! 否则将会多出n条SQL语句. 
     */
    @Test
    public void testCollectionSecondLevelCache() {
        Department dept1 = (Department) session.get(Department.class, 1004);
        System.out.println(dept1.getName());
        System.out.println(dept1.getEmps().size());

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

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

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

    /**
     * 3. 查询缓存
     */
    @Test
    public void testQueryCache() {
        Query query = session.createQuery("FROM Employee");
        // 设置查询缓存
        query.setCacheable(true);

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

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

    /**
     * 4. 更新时间戳缓存
     */
    @Test
    public void testUpdateTimestampCache() {
        Query query = session.createQuery("FROM Employee");
        // 设置查询缓存
        query.setCacheable(true);

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

        Employee employee = (Employee) session.get(Employee.class, 2012);
        employee.setName("qiaobei");

        emps = query.list();    // 更新完之后会发送SELECT语句
        System.out.println(emps.size());
    }

    /**
     * 5. Query接口的Iterate()方法
     *
     */
    @Test public void testQueryIterate() {
        Department dept1 = (Department) session.get(Department.class, 1004);
        System.out.println(dept1.getName());
        System.out.println(dept1.getEmps().size());

        Query query = session.createQuery("FROM Employee e WHERE e.dept.id = 1004");
//      List<Employee> emps = query.list();
//      System.out.println(emps.size()); // 仍会发送SELECT语句,因为二级缓存对查询操作无效

        Iterator<Employee> emps = query.iterate();
        while(emps.hasNext()) {
            System.out.println(emps.next());
        }
    }

    @Before
    public void init() {
        Configuration configuration = new Configuration().configure();

        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();

        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        session = sessionFactory.openSession();

        transaction = session.beginTransaction();
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }

}
Department.java:
package com.qiaobc.hibernate.hql;

import java.util.HashSet;
import java.util.Set;

public class Department {

    private Integer id;

    private String name;

    private Set<Employee> emps = new HashSet<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Employee> getEmps() {
        return emps;
    }

    public void setEmps(Set<Employee> emps) {
        this.emps = emps;
    }

    @Override
    public String toString() {
        return "Department [id=" + id + ", name=" + name + "]";
    }

}
Employee.java:
package com.qiaobc.hibernate.hql;

public class Employee {

    private Integer id;

    private String name;

    private float salary;

    private String email;

    private Department dept;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Department getDept() {
        return dept;
    }

    public void setDept(Department dept) {
        this.dept = dept;
    }

    public Employee(float salary, String email) {
        super();
        this.salary = salary;
        this.email = email;
    }

    public Employee() {
        super();
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", salary=" + salary
                + ", email=" + email + "]";
    }

}
hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <!-- 配置连接数据库的基本信息 -->
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql:///hibernate</property>

        <!-- 配置Hibernate的基本信息 -->
        <!-- 配置Hibernate所使用的数据库方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

        <!-- 执行操作时是否在控制台打印SQL语句 -->
        <property name="show_sql">true</property>

        <!-- 执行操作时是否对SQL语句进行格式化 -->
        <property name="format_sql">true</property>

        <!-- 执行操作时自动生成数据表的具体策略 -->
        <property name="hbm2ddl.auto">update</property>

        <!-- 1). 配置数据库的事务隔离级别为:READ UNCOMMITED -->
        <property name="hibernate.connection.isolation">1</property>

        <!-- 2). 配置数据库的事务隔离级别为:READ COMMITED -->
        <property name="hibernate.connection.isolation">2</property>

        <!-- 3). 配置数据库的事务隔离级别为:REPEATABLE READ -->
        <property name="hibernate.connection.isolation">4</property>

        <!-- 4). 配置数据库的事务隔离级别为:SERIALIZEABLE -->
        <property name="hibernate.connection.isolation">8</property>

        <!-- 删除对象后使其OID为null -->
        <property name="hibernate.use_identifier_rollback">true</property>

        <!-- 配置使用Hibernate二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache">true</property>

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

        <!-- 配置Hibernate二级缓存所使用的插件 -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

        <!-- 指定所关联的对象关系映射文件 -->
        <mapping resource="com/qiaobc/hibernate/hql/Employee.hbm.xml"/>
        <mapping resource="com/qiaobc/hibernate/hql/Department.hbm.xml"/>

        <!-- 配置对哪个类使用Hibernate的二级缓存 -->
        <!-- 
        <class-cache usage="read-write" class="com.qiaobc.hibernate.hql.Employee"/>
        <class-cache usage="read-write" class="com.qiaobc.hibernate.hql.Department"/>
        <collection-cache usage="read-write" collection="com.qiaobc.hibernate.hql.Department.emps"/>
         -->

    </session-factory>
</hibernate-configuration>
Department.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-2-24 20:06:22 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.qiaobc.hibernate.hql">

    <class name="Department" table="DEPARTMENTS">

        <cache usage="read-write"/>

        <id name="id" type="java.lang.Integer">
            <column name="DEPT_ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="DEPT_NAME" />
        </property>

        <set name="emps" table="EMPLOYEES" inverse="true">
            <cache usage="read-write"/>
            <!-- 当前表在EMPLOYEES表中的外键 -->
            <key column="DEPARTMENT_ID"></key>
            <one-to-many class="Employee"/>
        </set>

    </class>
</hibernate-mapping>
Employee.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2017-2-24 20:06:22 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.qiaobc.hibernate.hql">

    <class name="Employee" table="EMPLOYEES">

        <cache usage="read-write"/>

        <id name="id" type="java.lang.Integer">
            <column name="EMP_ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="EMP_NAME" />
        </property>

        <property name="salary" type="float">
            <column name="EMP_SALARY" />
        </property>

        <property name="email" type="java.lang.String">
            <column name="EMP_EMAIL" />
        </property>

        <many-to-one name="dept" class="Department" column="DEPARTMENT_ID"></many-to-one>

    </class>

    <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minsal AND e.salary < :maxsal]]></query>

</hibernate-mapping>
ehcache.xml:
<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <!-- path="d:\\temp" -->
    <!-- 指定一个目录:当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下 -->
    <diskStore path="java.io.tmpdir"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

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

    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.
        -->

    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <!-- 设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
        缓存区域: 一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
             如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。
        Hibernate在不同的缓存区域保存不同的类/集合。
            对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
            对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders 
     -->
    <!-- timeToIdleSeconds 设置对象空闲最大时间,以秒为单位,为0时表示可以无限期处于空闲状态 -->
    <!-- timeToLiveSeconds 设置对象生存最大时间,以秒为单位,为0时表示可以无限期处于缓存中 -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />

    <!-- Place configuration for your caches following -->

</ehcache>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值