Hibernate ——二级缓存

一、Hibernate 二级缓存

1.Hibernate 二级缓存是 SessionFactory 级别的缓存。

2.二级缓存分为两类:

(1)Hibernate内置二级缓存

(2)外置缓存,可配置的,可插拨的,外置缓存中的数据是数据库数据的复制。

3.二级缓存的并发访问策略

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

(2)二级缓存可以设定以下 4 中并发访问策略,每一种对应一种事务隔离级别。

  • 非严格读写(Nonstrict-read-write):不保证缓存与数据库中数据的一致性。对应 Read UnCommited 事务隔离级别。
  • 读写型(Read-write):提供 Read Commited 级别的事务隔离级别。
  • 事务型(Transactional):对应 Repeatable Read 级别的事务隔离级别。
  • 只读型(Read-Only):提供 Serializable 级别的数据隔离级别。

4.Hibernate 二级缓存是进程或集群范围内的缓存。是可配置的的插件。这里以 Ehcache 为例。不支持事务型的并发访问策略。

5.配置 Ehcache

(1)添加 Jar 包

(2)在 Hibernate 配置文件中启用二级缓存并指定适配的缓存适配器。

在 <session-factory> 元素内添加:

<property name="cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

(3)在 Hibernate 配置文件中配置需要使用二级缓存的持久化类,并设置它的二级缓存的并发访问策略。如:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>

需要注意的是:<session-factory> 元素内子元素的顺序:property*, mapping*, (class-cache|collection-cache)*, event*, listener*,class-cache 元素必须位于 mapping 节点后。

(4)或者也可以在 hbm 文件中配置,如:

<hibernate-mapping>

    <class name="com.solverpeng.hql.Employee" table="employee" schema="hibernate">
        <cache usage="read-write"/>
        <id name="empId" column="emp_id"/>
        <property name="empName" column="emp_name"/>
        <property name="salary" column="salary"/>
        <many-to-one name="dept" class="com.solverpeng.hql.Department">
            <column name="dept_id_fk" not-null="true"/>
        </many-to-one>
    </class>
</hibernate-mapping>

6. 测试 Hibernate 二级缓存

(1)查询单个对象缓存

@Test
public void testSecondCache() {
    Employee o = (Employee) session.get(Employee.class, 6);
    System.out.println(o);

    transaction.commit();
    session.close();
    session = factory.openSession();
    transaction = session.beginTransaction();

    Employee o1 = (Employee) session.get(Employee.class, 6);
    System.out.println(o1);
}

查询同一个对象两次,中间关闭 Session 再开启。

没有配置二级缓存:

Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
[email protected]
Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
[email protected]

结果:

发送了2条 SQL 语句。

配置二级缓存后(可以在Hibernate 配置文件中配置,也可以在 Employee hbm 配置文件中配置):

Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
[email protected]
[email protected]

结果:

只发送了1条 SQL 语句。

(2)集合缓存

@Test
public void testCollectionSecondLevelCache() {
    Department department = (Department) session.get(Department.class, 6);
    System.out.println(department.getDeptName());
    System.out.println(department.getEmps().size());

    transaction.commit();
    session.close();
    session = factory.openSession();
    transaction = session.beginTransaction();

    Department department2 = (Department) session.get(Department.class, 6);
    System.out.println(department2.getDeptName());
    System.out.println(department2.getEmps().size());

}

在没有配置二级缓存的情况下:

Hibernate:
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_
    from
        hibernate.department department0_
    where
        department0_.dept_id=?
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3
Hibernate:
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_
    from
        hibernate.department department0_
    where
        department0_.dept_id=?
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3

结果:

查询了两次 Department,两次Employee

只配置 Department 二级缓存:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
Hibernate:
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_
    from
        hibernate.department department0_
    where
        department0_.dept_id=?
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3

结果:

只查询了一次 Department,2次Employee。

说明:

开启 Department 二级缓存后,会对 Department 进行缓存,而与其关联的 emps 不会进行缓存。

配置 Department 二级缓存,同时配置关联的 emps 缓存。

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
Hibernate:
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_
    from
        hibernate.department department0_
    where
        department0_.dept_id=?
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3
-----------------------------
dept-aa
Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
Hibernate:
    select
        employee0_.emp_id as emp1_1_0_,
        employee0_.emp_name as emp2_1_0_,
        employee0_.salary as salary3_1_0_,
        employee0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee employee0_
    where
        employee0_.emp_id=?
3

结果:

发送了更多的查询 Employee 的SQL。

说明:

开启集合的二级缓存后,此时会缓存集合中对象的 id ,而不会对集合中的对象进行缓存。若想缓存,需要关联的集合中的对象也开启二级缓存。如:

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
<class-cache class="com.solverpeng.hql.Employee" usage="read-write"/>
Hibernate:
    select
        department0_.dept_id as dept1_0_0_,
        department0_.dept_name as dept2_0_0_
    from
        hibernate.department department0_
    where
        department0_.dept_id=?
dept-aa
Hibernate:
    select
        emps0_.dept_id_fk as dept4_0_1_,
        emps0_.emp_id as emp1_1_1_,
        emps0_.emp_id as emp1_1_0_,
        emps0_.emp_name as emp2_1_0_,
        emps0_.salary as salary3_1_0_,
        emps0_.dept_id_fk as dept4_1_0_
    from
        hibernate.employee emps0_
    where
        emps0_.dept_id_fk=?
3
-----------------------------
dept-aa
3

结果:

除 Department 外,关联的 Employee 也被缓存了。

(3)查询缓存(针对 HQL、QBC)

在二级缓存开启的情况下,HQL、QBC 也不能对查询进行缓存。

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

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

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate:
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_
    from
        hibernate.employee employee0_
12
Hibernate:
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_
    from
        hibernate.employee employee0_
12

开启查询缓存:

  • 在 Hibernate 配置文件中开启查询缓存
  • 若想启用查询缓存的查询语句,需要调用 Query 或 Criteria 的 setCacheable() 方法。
  • 查询缓存依赖于二级缓存
<property name="hibernate.cache.use_query_cache">true</property>
@Test
public void testQueryCache() {
    Query query = session.createQuery("from Employee ");
    query.setCacheable(true);
    List<Employee> list = query.list();
    System.out.println(list.size());

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate:
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_
    from
        hibernate.employee employee0_
12
12

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

@Test
public void testTimStampCache() {
    Query query = session.createQuery("from Employee ");
    query.setCacheable(true);
    List<Employee> list = query.list();
    System.out.println(list.size());

    Employee employee = (Employee) session.get(Employee.class, 6);
    employee.setEmpName("[email protected]@");

    List<Employee> emps2 = query.list();
    System.out.println(emps2.size());
}
Hibernate:
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_
    from
        hibernate.employee employee0_
12
Hibernate:
    update
        hibernate.employee
    set
        emp_name=?,
        salary=?,
        dept_id_fk=?
    where
        emp_id=?
Hibernate:
    select
        employee0_.emp_id as emp1_1_,
        employee0_.emp_name as emp2_1_,
        employee0_.salary as salary3_1_,
        employee0_.dept_id_fk as dept4_1_
    from
        hibernate.employee employee0_
12

二、ehcache.xml

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

1.<diskStore>: 指定一个目录:当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.

2.<defaultCache>: 设置缓存的默认数据过期策略

3.<cache> 设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域

4.缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>

5.Hibernate在不同的缓存区域保存不同的类/集合。

  • 对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
  • 对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders

6.cache 元素的属性

(1)name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字

(2)maxInMemory:设置基于内存的缓存中可存放的对象最大数目

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

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

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

(6)overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中。

三、管理 Session

–Session 对象的生命周期与本地线程绑定

–Session 对象的生命周期与 JTA 事务绑定

–Hibernate 委托程序管理 Session 对象的生命周期

四、批量处理数据

建议通过 JDBC 的方式来进行批量操作。

@Test
public void testBathch() {
    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            // 执行批量操作
        }
    });
}

五、总结

配置 Hibernate 二级缓存的步骤:

1.配置 Hibernate 配置文件

(1)配置启用二级缓存

<property name="cache.use_second_level_cache">true</property>

(2)配置二级缓存使用的产品

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

(3)配置对哪些类使用 hibernate 的二级缓存以及并发策略

<class-cache class="com.solverpeng.hql.Department" usage="read-write"/>
<collection-cache collection="com.solverpeng.hql.Department.emps" usage="read-write"/>
<class-cache class="com.solverpeng.hql.Employee" usage="read-write"/>

(4)在 hbm 文件中配置缓存

<set name="emps" inverse="true">
    <cache usage="read-write"/>
    <key>
        <column name="dept_id_fk" not-null="true"/>
    </key>
    <one-to-many not-found="ignore" class="com.solverpeng.hql.Employee"/>
</set>

2. 对于集合缓存来说,还需要配置集合中的元素对应的持久化类也使用二级缓存! 否则将会多出 n 条 SQL 语句.

3. 查询缓存

(1)在 Hibernate 配置文件中开启查询缓存支持:<property name="cache.use_query_cache">true</property>

(2)调用 Query 或 Criteria 的 setCacheable(true)方法

(3)查询缓存依赖于二级缓存

时间: 2024-10-25 09:47:17

Hibernate ——二级缓存的相关文章

Hibernate二级缓存以及ehcache的搭建配置

前言 这次主要复习Hibernate的二级缓存的相关知识,配置以及使用.二级缓存主要采用第三方的ehcache,也将介绍ehcache缓存的相关配置属性以及在项目中的搭建,具体的项目查看下一篇的 Maven搭建SpringMVC+Hibernate项目详解 的文章.(之前使用过Hibernate的二级缓存,但是没自己搭建和研究过,现在花了半天时间搭建了一下,写下来供大家参考) 1.Hibernate二级缓存 Hibernate包括两个级别的缓存: 1.一级缓存:默认总是启用的session级别的

Hibernate二级缓存的使用

1启用Hibernate二级缓存 Hibernate二级缓存分为两部分,class缓存和查询缓存,其获取对象的方式有所不同,但两者也有联系,查询缓存必须以class缓存为基础才能起作用,否则只会使效率更低. 我们这里使用的二级缓存是通过ehcache第三方插件实现的. 1.1配置Hibernate.cfg.xml 启用class缓存: <property name="hibernate.cache.provider_class"> org.hibernate.cache.E

Hibernate(十六):Hibernate二级缓存(一)

Hibernate缓存 缓存(Cache):计算机领域非常通用的概念.它介于应用程序和永久性数据存储源(如磁盘上的文件或者数据库)之间,起作用是降低应用程序直接读取永久性数据存储源的频率,从而提高应用的运行性能.缓存中的数据是数据存储源中数据的拷贝.缓存的物理介质通常是内存. Hibernate中提供了两个级别的缓存 1)第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存.这一级别的缓存由hibernate管理. 2)第二级别的缓存是SesssionFactory级别的缓存,它是属

hibernate二级缓存ehcache

<!-----------------hibernate二级缓存ehcache------------------------->hibernate配置 <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.use_second_level_cache">true</prop> <prop

Hibernate 二级缓存 总结整理(转)

和<Hibernate 关系映射 收集.总结整理> 一样,本篇文章也是我很早之前收集.总结整理的,在此也发上来 希望对大家有用.因为是很早之前写的,不当之处请指正. 1.缓存:缓存是什么,解决什么问题? 位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为 Cache(摘自Robbin的<缓存技术浅谈>).目的:让数据更接近于应用程序,协调速度不匹配,使访问速度更快.(请参考http://baike.baidu.com/view/907.htm 了

Hibernate二级缓存

第一级缓存:事务范围的缓存.Session缓存.存放元数据和预定义SQL.只读缓存. 第二级缓存:进程范围或者集群范围.由SessionFactory负责管理.SessionFactory的外置缓存.SessionFactory这个级别维护的缓存.二级缓存是针对整个应用而不是某个特定的session. Session 如何判断持久化对象的状态的改变呢? Session 加载对象后会为对象值类型的属性复制一份快照.当Session 清理缓存时,比较当前对象和它的快照就可以知道那些属性发生了变化.

hibernate二级缓存攻略(转)

很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了. 我的经验主要来自hibernate2.1版本,基本原理和3.0.3.1是一样的,请原谅我的顽固不化. hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了. 二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehc

Hibernate二级缓存以及HQL(三)

Hibernate检索方式 1:Hibernate 提供了以下检索对象的方式 1.导航对象图检索方式:在程序中获得持久的实体对象,通过对象关联,获得相关的实体对象.比如:session.get(Department.class,1); dept.getEmps() 2.OID检索方式:session的get或load根据OID查找对象 3.HQL检索方式:Query接口.使用面向对象的HQL语句进行查询 4.QBC(Query By Criteria)检索方式:Criteria接口.提供了更加面

Hibernate学习---第十五节:hibernate二级缓存

1.二级缓存所需要的 jar 包 这三个 jar 包实在 hibernate 解压缩文件夹的 lib\optional\ehcache 目录下 2.配置 ehcache.xml <ehcache> <!-- 指定当缓存数据超出规定缓存大小的时候,超出的缓存数据写入到磁盘的位置 --> <diskStore path="D:/cache/tmp"/> <!-- maxInMemory - 允许在二级缓存中的持久化对象数量 eternal - 缓存