首先要明确一下几点信息:
1,缓存的概念:
缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存或者硬盘中的容器,其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用程序的运行性能。Hibernate 在进行读取数据的时候,根据缓存机制在相应的缓存中查询,如果在缓存中找到了需要的数据(我们把这称做“缓存命中"),则就直接把命中的数据作为结果加以利用,避免了大量发送SQL 语句到数据库查询的性能损耗。
2,Hibernate 缓存的分类:
1、Session缓存(又称作事务缓存):Hibernate内置的,不能卸除。
缓存范围:缓存只能被当前Session对象访问。缓存的生命周期依赖于Session的生命周期,当Session被关闭后,缓存也就结束生命周期。
2、SessionFactory缓存(又称作应用缓存):使用第三方插件,可插拔。
缓存范围:缓存被应用范围内的所有session 共享,不同的Session 可以共享。这些session 有可能是并发访问缓存,因此必须对缓存进行更新。缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命周期,二级缓存存在于应用程序范围。
3,二级缓存策略提供商:
提供了HashTable缓存,EHCache,OSCache,SwarmCache,jBoss Cathe2,这些缓存机制,其中EHCache,OSCache 是不能用于集群环境(Cluster Safe)的,而SwarmCache,jBoss Cathe2 是可以的。HashTable 缓存主要是用来测试的,只能把对象放在内存中,EHCache,OSCache 可以把对象放在内存(memory)中,也可以把对象放在硬盘(disk)上(为什么放到硬盘上?上面解释了)。
4,什么数据适合放二级缓存中:
(1)经常被访问
(2)改动不大
(3)数量有限
(4)不是很重要的数据,允许出现偶尔并发的数据。
比如组织机构代码,列表信息等;
一、新建工程Hibernate11-02
二、新建学生类Student:
package com.test.model;
public class Student {
private long id;
private String name;
private Class c;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class getC() {
return c;
}
public void setC(Class c) {
this.c = c;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
新建班级类Class:
package com.test.model;
public class Class {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.Student类配置文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name" column="stuName"></property>
<many-to-one name="c" column="classId" class="com.test.model.Class" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
3,写Class映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.model">
<class name="Class" table="t_class">
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
</class>
</hibernate-mapping>
4.把这两个类的映射文件添加到hibernate配置文件中,写测试方法:
package com.test.service;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.test.model.Class;
import com.test.model.Student;
import com.test.util.HibernateUtil;
public class StudentTest {
private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testCache1() {
}
}
运行testCache1生成数据表,并往数据表t_class中添加测试数据如下:
修改测试方法:
@Test
public void testCache1() {
Class c = (Class) session.get(Class.class, Long.valueOf(1));
System.out.println(c.getName());
Class c2 = (Class) session.get(Class.class, Long.valueOf(1));
System.out.println(c2.getName());
System.out.println(c==c2);
}
运行程序:
可以看到这里只运行了一次sql查询,在第一次运行sql语句查询出班级的信息后会把班级信息保存在session缓存中,当第二次取相同的班级信息时先在session缓存中取,发现该对象后直接使用,这两个对象指向了同一个OID对象。
写一个新的测试方法:
@Test
public void testCache2() {
Session session1 = sessionFactory.openSession();
session1.beginTransaction();
Class c = (Class) session1.get(Class.class, Long.valueOf(1));
System.out.println(c.getName());
session1.getTransaction().commit();
session1.close();
Session session2 = sessionFactory.openSession();
session2.beginTransaction();
Class c2 = (Class) session2.get(Class.class, Long.valueOf(1));
System.out.println(c2.getName());
session2.getTransaction().commit();
session2.close();
}
运行这个测试方法:
这里执行了两次sql查询,取到的两个对象为OID不同的两个对象。因为使用的session缓存不同,这两个对象占据了不同的内存空间。
上面这个例子可以看出一级session缓存是不能够共用的,不同的session使用不同的session缓存。
三、配置EHCache二级缓存
上面的例子知道不同的session使用不同的一级缓存,是不能够共享同一个一级缓存的。如果想让他们共享同一个缓存,就要用sessionfactory二级缓存了。下面配置一个EHCache二级缓存。
1.工程中添加jar包:在hibernate的安装文件中给我们提供了ehcache的jar包:hibernate-release-4.3.5.Final\lib\optional\ehcache文件夹下。在工程根目录下新建文件夹eacache,把jar包放到该文件夹下,并添加到工程中:
2.添加eacache配置文件:可以在hibernate安装包里面的demo中直接复制一个:
配置文件内容为:
<ehcache>
<!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个目录下 -->
<diskStore path="c:\\ehcache"/>
<!--
设置缓存的默认数据过期策略
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<!--
name 设置缓存的名字,他的取值为类的完整名字或者类的集合的名字;
maxElementsInMemory 设置基于内存的缓存可存放的对象的最大数目
eternal 如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds,默认为false;
timeToIdleSeconds 设定允许对象处于空闲状态的最长时间,以秒为单位;
timeToLiveSeconds 设定对象允许存在于缓存中的最长时间,以秒为单位;
overflowToDisk 如果为true,表示当基于内存的缓存中的对象数目达到maxElementsInMemory界限,会把溢出的对象写到基于硬盘的缓存中;
-->
<!-- 设定具体的第二级缓存的数据过期策略 -->
<cache name="com.test.model.Class"
maxElementsInMemory="1"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
/>
</ehcache>
修改hibernate的配置文件:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//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/hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 控制台显示SQL -->
<property name="show_sql">true</property>
<!-- 自动更新表结构 -->
<property name="hbm2ddl.auto">update</property>
<!-- 启用二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 配置使用的二级缓存的产品 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<!-- 配置启用查询缓存 -->
<property name="cache.use_query_cache">true</property>
<mapping resource="com/test/model/Student.hbm.xml"/>
<mapping resource="com/test/model/Class.hbm.xml"/>
</session-factory>
</hibernate-configuration>
修改Class的映射文件:
<hibernate-mapping package="com.test.model">
<class name="Class" table="t_class">
<cache usage="read-only"/>
<id name="id" column="classId">
<generator class="native"></generator>
</id>
<property name="name" column="className"></property>
</class>
</hibernate-mapping>
这里添加了一个<cache>标签,来启动二级缓存。这样的话Class类的对象会保存到二级缓存中。
再次运行testCache2测试方法:
可以看到这里只执行了一条sql查询语句了,这里二级缓存起作用了。由于使用了不同的session,这两个对象的OID标识符不同,所以他们是不相同的。不同的session是共享同一个二级缓存的,所以这里也就没有执行新的sql查询语句。