hibernate系列十四:hibernate缓存

一.   hibernate一级缓存

 Session内的缓存即一级缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应。Session能够在某些时间点,按照缓存中对象的变化来执行相关SQL语句,从而同步更新数据库,这一过程称为刷新缓存。

    当应用程序调用Session的save()、update()、saveOrUpdate()、load()或get()方法,以及调用Query查询接口的list()或iterate()方法时,如果在Session的缓存中还不存在相应的对象,Hibernate就会把该对象加入到一级缓存中。在刷新缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。由此可见,Session缓存有两大作用:

    (1)减少访问数据库的频率。

    (2)保证数据库中的相关记录与缓存中的相应对象同步。

案例1

package com.obtk.test;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import com.obtk.entitys.StudentEntity;
import com.obtk.utils.HiberUtil;

public class CacheOneTest1 {
	public static void main(String[] args) {
		Session session=null;
		try {
			session=HiberUtil.getSession();
			StudentEntity stu1=(StudentEntity)session.get(StudentEntity.class, 119);
			System.out.println("===========这是华丽的分割线================");
			//HiberUtil.closeSession();
			//session=HiberUtil.getSession();
			//默认应用一级缓存,直接到内存里面找对象
			StudentEntity stu2=(StudentEntity)session.get(StudentEntity.class, 119);
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HiberUtil.closeSession();
		}
	}
}

观察结果,发现自动应用了一级缓存,只有一个sql输出

Hibernate: 
    select
        studentent0_.stuId as stuId2_0_,
        studentent0_.stuName as stuName2_0_,
        studentent0_.gender as gender2_0_,
        studentent0_.age as age2_0_,
        studentent0_.address as address2_0_,
        studentent0_.deptIdd as deptIdd2_0_ 
    from
        student studentent0_ 
    where
        studentent0_.stuId=?
===========这是华丽的分割线================

关闭session,再打开session后,发现会输出2条sql,因为session范围内的缓存被清除了

Hibernate: 
    select
        studentent0_.stuId as stuId2_0_,
        studentent0_.stuName as stuName2_0_,
        studentent0_.gender as gender2_0_,
        studentent0_.age as age2_0_,
        studentent0_.address as address2_0_,
        studentent0_.deptIdd as deptIdd2_0_ 
    from
        student studentent0_ 
    where
        studentent0_.stuId=?
===========这是华丽的分割线================
Hibernate: 
    select
        studentent0_.stuId as stuId2_0_,
        studentent0_.stuName as stuName2_0_,
        studentent0_.gender as gender2_0_,
        studentent0_.age as age2_0_,
        studentent0_.address as address2_0_,
        studentent0_.deptIdd as deptIdd2_0_ 
    from
        student studentent0_ 
    where
        studentent0_.stuId=?

二    hibernate二级缓存

二级缓存是进程或集群范围内的缓存,可以被所有的Session共享,其生命周期和SessionFactory一样。二级缓存是可配置的插件。Hibernate打包一些开源缓存实现,提供对它们的内置支持,如表1所示。

缓存插件缓存实现类查询缓存类  型
EHCacbeorg.hibernate.cache.EhCacheProvider支持

进程范围的缓存;

内存或硬盘
OSCacheorg.hibernate.cache.OSCacheProvider支持

进程范围的缓存;

内存或硬盘
SwarnCacheorg.hibernate.cache.SwarnCacheProvider不支持集群范围的缓存
JBossCacheorg.bibernate.cache.TreeCacheProvider支持集群范围的缓存
为了把表1中的缓存插件集成到Hibernate中,Hibernate提供了CacheProvider接口,它是缓存插件与Hibernate之间的适配器。表1中的缓存实现类就是CacheProvider的不同实现。
配置二级缓存的步骤如下。
    (1)选择合适的缓存插件,配置其自带的配置文件。
    (2)选择需要使用二级缓存的持久化类,设置它的二级缓存的并发访问策略。
以EHCache配置为例,步骤如下。
1.将ehcache.xml文件添加到类路径下。
在路径hibernate_distribution-3.3.2.GA\project\etc下复制ehcache.xml。
<cache>标签为每个需要二级缓存的类和集合设定缓存的数据过期策略,配置如下。
<cache name="sampleCache1"      --缓存的名称,取值为累的完整名称或类的集合的名称
maxElementsInMemory="1000"  --基于缓存可存放的对象的最大数目
eternal="true"              --如果设为true,表示对象永不过期,默认为false
timeToIdleSeconds="0"       --设置允许对象处于空闲状态的最长时间,单位是秒
timeToLiveSeconds="0"       --设置对象允许存在于缓存的最长时间,单位是秒
overflowToDisk="false"      --是否将溢出的对象写到基于硬盘的缓存中
/>


2.开启二级缓存:
<property name="hibernate.cache.use_second_level_cache">true</property>
3.指定缓存产品提供商:
<property name="hibernate.cache.provider_class">
    org.hibernate.cache.EhCacheProvider
</property>


4.指定使用二级缓存的持久化类。修改持久化类的映射文件,为<class>元素添加<cache>元素,配置如下。
<class name="Student" table="STUDENT">
    <cache usage="" region="" include=""/>
    ……
</class>


<cache>的属性:
usage属性是必需的,指定并发访问策略,取值为transactional(事务缓存)、  
read-write(读/写缓存)、nonstrict-read-write(非严格读/写缓存)或read-only(只读缓存)。
region属性可选,是ehcache.xml中cache标签的name值。
include属性可选,取值为non-lazy(当缓存一个对象时,不会缓存它的映射为延迟加载的属性)、all,默认取值为all。


注意    配置EHCache二级缓存时,需要添加如下jar包到程序中:
(1) ehcache-1.2.3.jar,  EHCache包。
(2) commons-logging-1.1.1.jar,日志包。


示例l2使用二级缓存查询DepartEntity对象。实现步骤如下:
(1)添加ehcache-1.2.3.jar及commons-logging-1.1.1jar到课程中。
(2)将ehcache.xml文件添加到src下。
(3)配置hibernate.cfg.xml及持久化类映射文件。
(4)编写测试代码。
示例
关键代码:
=====================ehcache.xml===================

<ehcache>

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

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

  
    <cache name="stu"
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

</ehcache>
==========================hibernate.cfg.xml==========================
<!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.url">
		jdbc:mysql://localhost:3306/schooldb
	</property>
	<property name="connection.username">root</property>
	<property name="connection.password">123456</property>
	<property name="connection.driver_class">
		com.mysql.jdbc.Driver
	</property>
	<property name="dialect">
		org.hibernate.dialect.MySQLDialect
	</property>
	<property name="show_sql">true</property>
	<property name="format_sql">true</property>
	<property name="current_session_context_class">thread</property>
	
	<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
	<property name="hibernate.cache.use_second_level_cache">true</property>
	
	<mapping resource="com/obtk/entitys/UserEntity.hbm.xml" />
	<mapping resource="com/obtk/entitys/Department.hbm.xml" />
	<mapping resource="com/obtk/entitys/Student.hbm.xml" />
</session-factory>
</hibernate-configuration>
然后在映射文件中开启缓存

============================student.hbm.xml=======================

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping package="com.obtk.entitys">
    <class name="StudentEntity" table="student">
    	<cache usage="read-write" region="stu"/>
        <id name="stuId" type="java.lang.Integer">
            <column name="stuId" />
            <generator class="native"></generator>
        </id>
        <property name="stuName" type="java.lang.String">
            <column name="stuName" length="20" />
        </property>
        <property name="gender" type="java.lang.String">
            <column name="gender" length="2" />
        </property>
        <property name="age" type="integer">
            <column name="age" />
        </property>
        <property name="address" type="java.lang.String">
            <column name="address" length="200" />
        </property>
        <many-to-one name="dept" class="DeptEntity"
        	column="deptIdd" lazy="proxy"></many-to-one>
    </class>
</hibernate-mapping>
==================测试代码=====================

package com.obtk.test;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.obtk.entitys.DeptEntity;
import com.obtk.entitys.StudentEntity;
import com.obtk.utils.HiberUtil;

public class CacheTwoTest1 {
	public static void main(String[] args) {
		Session session=null;
		try {
			session=HiberUtil.getSession();
			//如果开启了二级缓存,stu1在整个应用范围内都是共享的
			StudentEntity stu1=(StudentEntity)session.get(StudentEntity.class, 119);
			HiberUtil.closeSession();  //关闭了session,一级缓存肯定失效
			session=HiberUtil.getSession();
			//默认应用一级缓存,直接到内存里面找对象
			StudentEntity stu2=(StudentEntity)session.get(StudentEntity.class, 119);
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HiberUtil.closeSession();
		}
	}
}

运行示例,即使在不同Session范围内获得同- StudentEntity对象,控制台也只输出一条seiect查询语句,证明配置二级缓存成功。
    有时候考虑到内存开销的问题,需要关闭与二级缓存的交互。例如,在上面的示例中,当使用Session进行批量操作时,
可以调用Session的setCacheModefCacheMode.IGNORE)方法关闭与二级缓存的交互。CacheMode.IGNORE参数的意思是当前Session和二级缓存不再相互作用。
    二级缓存并非适合所有场景,使用不当,反而会降低性能。符合如下条件的数据适合放进二级缓存:
很少修改的数据。
不是很关键的数据,能容忍短时间内读到过期数据。
不会在多个应用之间共享的数据。
应用参考的常量数据。它的实例数目有限,实例会被许多其他类的实例引用,实例极少或从来不会被修改。
以下数据不适合放进二级缓存:
经常修改的数据。
财务数据,绝对不允许读到过期数据。
与其他应用共享的数据。如果其他应用修改了数据库中的数据,Hibernate无法自动保证二级缓存中的数据与数据库一致。


三  查询缓存

前面我们讲的Hibernate的二级缓存,只有在基于ID查找对象时才会用到,对于查询则毫无作用。为此,Hibernate提供了针对查询的查询缓存。查询缓存依赖于二级缓存,因此使用查询缓存之前要按步骤配置好二级缓存。

    使用查询缓存的步骤如下。

(1)在hibernate.cfg.xml中开启查询缓存:

<propertyname="hibernate.cache.use_query_cache">true</property>

(1) 在程序中启用查询缓存:

query.setCacheable(true);

示例

使用查询缓存查询lD为2202的学生信息。

关键代码:

hibernate.cfg.xml关键代码:

   <property name="hibernate.cache.use_query_cache">true</property>

StudentEntity.hbm.xml关键代码:

<classname="com.hopetech.hibernate.entity.StudentEntity"table="STUDENT">

    <cache usage="read-write"/>

    ……

</class>

 

测试类关键代码:

   session= sessionFactory.openSession();

   Query query=session.createQuery("fromStudentEntity where stuId=2202");

   List<StudentEntity> students=query.setCacheable(true).list();

   for(StudentEntity student:students){

     System.out.println(student.getStuName());

   }

   session.close();

   session= sessionFactory.openSession();

   Query query2=session.createQuery("fromStudentEntity where stuId=2202");

   List<StudentEntity> students2=query.setCacheable(true).list();

   for(StudentEntity student:students2){

     System.out.println(student.getStuName());

   }

    运行示例,即使在不同Session范围内使用条件查询两次获得同一学生信息,控制台也只输出一条select语句,证明配置查询缓存成功。

  对于经常使用的查询语句,可以启用查询缓存,当第一次执行查询语句时,Hibernate会把查询结果存放到查询缓存中。以后再执行此查询语句时,只需要从缓存中获得查询结果即可,从而提高查询性能。

    查询缓存适用的场合:

    (1)经常使用的查询语句。

    (2)对查询到的数据很少有插入、删除或更新操作。

注意:查询缓存并不适合缓存关联的对象




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值