Hibernate提供了两级缓存,第一级缓存是Session的缓存。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必须的,不允许而且事实上也无法被卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID
第二级缓存是一个可插拔的缓存插件,它由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个进程对应,因此第二级缓存是进程范围的缓存。这个缓存中存放的是对象的散装数据。第二级缓存是可选的,可以在每个类或者每个集合的粒度上配置第二级缓存。
Hibernate的二级缓存结构
Hibernate中的EhCache类
Hibernate二级缓存:
1)transactional:必须在受管的环境下使用(针对容器来说,tomcat就不行),保证可重复读的事务隔离级别,对于读/写比例大,很少去更新的数据通常可以采取这种方式
2)read-write:使用timestamp机制维护已提交事务隔离级别,对于读/ 写比例大,很少更新的数据通常可以采用这种方式。
3)nonstrict-read-write:二级缓存与数据库中的数据可能会出现不一致的情况。在使用这种策略的时候,应该设置足够短的缓存过期时间,否则就有可能从缓存中读到脏数据。当一些数据很少改变(一天两天都不改变的数据),并且这些数据如果出现数据库与缓存不一致的情况下影响并不大的时候,那么可以采取这种缓存策略。
4)read-only:当确定数据不会被改变的时候,我们可以使用这种缓存策略。
实验:
以Team和Student为例:
import java.util.Date; public class Student { private String id; private String cardId; private String name; private int age; private Team team; public Team getTeam() { return team; } public void setTeam(Team team) { this.team = team; } public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getCardId() { return cardId; } public void setCardId(String cardId) { this.cardId = cardId; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } }
import java.util.HashSet; import java.util.Set; public class Team { private String id; private String teamName; private Set students = new HashSet(); public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTeamName() { return teamName; } public void setTeamName(String teamName) { this.teamName = teamName; } public Set getStudents() { return students; } public void setStudents(Set students) { this.students = students; } }
<?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">
<hibernate-mapping>
<class name="com.cdtax.hibernate.Student" table="student">
<cache usage="read-only"/>
<id name="id" column="id" type="string">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="cardId" column="cardid" type="string"></property>
<property name="age" column="age" type="int"></property>
<many-to-one name="team" class="com.cdtax.hibernate.Team" column="team_id"></many-to-one>
</class>
</hibernate-mapping>
<?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">
<hibernate-mapping>
<class name="com.cdtax.hibernate.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid">
</generator>
</id>
<property name="teamName" column="teamname" type="string"></property>
<set name="students" lazy="true" cascade="all" inverse="true" fetch="select">
<cache usage="read-only"/><!-- 使用缓存策略,只需要加上这个标签就可以了,比较简单 -->
<!-- 但是现在缓存的只是student的id,而不是所有属性,还需要在Student.hbm.xml中配置缓存 -->
<key column="team_id"></key>
<one-to-many class="com.cdtax.hibernate.Student"/>
</set>
</class>
</hibernate-mapping>
使用二级缓存,还需要配置一个ehcache.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="c:/ehcache" />
<defaultCache maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true" />
<cache name="com.cdtax.hibernate.Student" maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
同时,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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/hibernate
</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<mapping resource="Student.hbm.xml" />
<mapping resource="Team.hbm.xml"/>
</session-factory>
</hibernate-configuration>
在student中插入大量数据:一个班级,1200个学生
try { tx = session.beginTransaction(); Team team = new Team(); team.setTeamName("team2"); for(int i = 0;i < 1200; i++) { Student student = new Student(); student.setCardId("123465"); student.setAge(40); student.setName("zhangsan" + i); student.setTeam(team); team.getStudents().add(student); session.save(team); } tx.commit(); }
执行一个查询操作:
try { // tx = session.beginTransaction(); // // Team team = new Team(); // team.setTeamName("team2"); // // for(int i = 0;i < 1200; i++) // { // Student student = new Student(); // // student.setCardId("123465"); // student.setAge(40); // student.setName("zhangsan" + i); // student.setTeam(team); // team.getStudents().add(student); // // session.save(team); // } Session session1 = sessionFactory.openSession(); Transaction tx1 = session.beginTransaction(); List<Student> list = session.createQuery("from Student").list(); for(Student student : list) { System.out.println(student.getName()); } System.out.println("-------------------------"); tx1.commit(); Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); Student s1 = (Student)session2.get(Student.class, "402881c04259385e014259385f360004"); Student s2 = (Student)session2.get(Student.class, "402881c04259385e014259385f360005"); System.out.println(s1.getName()); System.out.println(s2.getName()); }
我们开启了两个session进行了查询,查看执行结果:
Hibernate:
select
student0_.id as id0_,
student0_.name as name0_,
student0_.cardid as cardid0_,
student0_.age as age0_,
student0_.team_id as team5_0_
from
student student0_
zhangsan0
zhangsan112
zhangsan697
。
。
。
-------------------------
zhangsan697
zhangsan1106
可以看到,只执行一条查询语句,session2的查询没有发出查询语句,而是直接从二级缓存中取得
我们将hibernate.cfg.xml、Team.hbm.xml和Student.hbm.xml的二级缓存的配置项去掉,如下
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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/hibernate
</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!--
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
-->
<mapping resource="Student.hbm.xml" />
<mapping resource="Team.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">
<hibernate-mapping>
<class name="com.cdtax.hibernate.Student" table="student">
<!-- <cache usage="read-only"/> -->
<id name="id" column="id" type="string">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" type="string"></property>
<property name="cardId" column="cardid" type="string"></property>
<property name="age" column="age" type="int"></property>
<many-to-one name="team" class="com.cdtax.hibernate.Team" column="team_id"></many-to-one>
</class>
</hibernate-mapping>
Team.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">
<hibernate-mapping>
<class name="com.cdtax.hibernate.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid">
</generator>
</id>
<property name="teamName" column="teamname" type="string"></property>
<set name="students" lazy="true" cascade="all" inverse="true" fetch="select">
<!-- <cache usage="read-only"/> --><!-- 使用缓存策略,只需要加上这个标签就可以了,比较简单 -->
<!-- 但是现在缓存的只是student的id,而不是所有属性,还需要在Student.hbm.xml中配置缓存 -->
<key column="team_id"></key>
<one-to-many class="com.cdtax.hibernate.Student"/>
</set>
</class>
</hibernate-mapping>
再次执行上述查询:
Hibernate:
select
student0_.id as id0_,
student0_.name as name0_,
student0_.cardid as cardid0_,
student0_.age as age0_,
student0_.team_id as team5_0_
from
student student0_
order by
student0_.name asc
zhangsan0
zhangsan1
。
。
。
-------------------------
Hibernate:
select
student0_.id as id0_0_,
student0_.name as name0_0_,
student0_.cardid as cardid0_0_,
student0_.age as age0_0_,
student0_.team_id as team5_0_0_
from
student student0_
where
student0_.id=?
Hibernate:
select
student0_.id as id0_0_,
student0_.name as name0_0_,
student0_.cardid as cardid0_0_,
student0_.age as age0_0_,
student0_.team_id as team5_0_0_
from
student student0_
where
student0_.id=?
zhangsan697
zhangsan1106
大家可以看到session2的查询发出了两个sql语句,说明是从数据库中取得,而不是从缓存中取得。
查看我们配置的缓存diskStore,即C:\ehcache目录,有两个文件:
com.cdtax.hibernate.Student.data ——这就是缓存文件
com.cdtax.hibernate.Team.students.data