Hibernate缓存详解
1. 什么是缓存?
数据库的缓存指的是应用程序和物理数据源之间的数据。即把物理数据源的数据复制到缓存。有了缓存,可以降低应用程序对物理数据源的访问频率,从而提高效率。缓存的介质一般是内存,也可以是硬盘。
hibernate的缓存有三种类型:一级缓存、二级缓存和查询缓存。
2. 一级缓存
一级缓存即session缓存,由session自动进行管理,不需要程序进行干预。一级缓存根据对象的id进行加载和缓存。如下面的代码:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); course c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); tx.commit(); session.close(); }
运行结果:
hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? name:计算机原理 name:计算机原理
第1次查询时生成了sql语句,并将查询出来的对象放在一级缓存里面,第2次查询时,在一级缓存里面直接找到了这个对象,就不需要再次生成sql语句了。
再看一个例子:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); course c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); tx.commit(); session.close(); session = sessionfactory.opensession(); tx = session.begintransaction(); c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); tx.commit(); session.close(); }
由于一级缓存是session级别的缓存,所以session关闭以后,一级缓存也就不存在了,第2次查询也要生成sql语句:
hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? name:计算机原理 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? name:计算机原理
3. 二级缓存
二级缓存即sessionfactory缓存,和一级缓存类似,也是根据对象的id进行加载和缓存,区别就在于一级缓存只在session内有效,而二级缓存在sessionfactory内有效。在访问某个id的对象时,先到一级缓存里面去找,如果没有找到就到二级缓存里面去找。二级缓存包括ehcache,oscache,swarmcache和jbosscache等。这里以ehcache作为例子。
二级缓存需要程序进行管理。首先,配置maven下载相关的jar,在pom文件里面添加:
<dependency> <groupid>org.hibernate</groupid> <artifactid>hibernate-ehcache</artifactid> <version>4.1.0.final</version> </dependency> <dependency> <groupid>net.sf.ehcache</groupid> <artifactid>ehcache</artifactid> <version>2.8.3</version> </dependency>
创建ehcache配置文件ehcache.xml:
<ehcache> <diskstore path="e:\eclipse\myworkspace\cache"/> <defaultcache maxelementsinmemory="10000" eternal="true" timetoidleseconds="120" timetoliveseconds="120" overflowtodisk="true" /> <cache name="com.hzhi.course.entity.course" maxelementsinmemory="10000" eternal="true" timetoidleseconds="300" timetoliveseconds="600" overflowtodisk="true" /> </ehcache>
defaultcache是默认的设置,下面一个cache指明了对哪一个类进行二级缓存。里面设置了最大缓存的对象数量,是否永久有效、最大空闲秒数、最大生存秒数、内存满时是否写到硬盘,写到硬盘的路径等等。
修改需要缓存的类的hbm文件:
<class name="com.hzhi.course.entity.course" table="clas"> <cache usage="read-only"/> ...... </class>
usage设置了并发访问策略,一般设置成read-only。
修改applicationcontext.xml中的sessionfactory的配置,增加二级缓存的一些属性:
<!-- sessionfactory --> <bean id="sessionfactory" class="org.springframework.orm.hibernate4.localsessionfactorybean"> <property name="datasource" > <ref local="datasource"/> </property> <!-- 配置hibernate的属性 --> <property name="hibernateproperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.mysqldialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.connection.isolation">8</prop> <!-- 二级缓存 --> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.ehcacheregionfactory</prop> <prop key="hibernate.cache.provider_configuration_file_resource_path">web-inf/ehcache.xml</prop> </props> </property> ...... </bean>
运行下面的例子:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); course c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); tx.commit(); session.close(); session = sessionfactory.opensession(); tx = session.begintransaction(); c = (course) session.get(course.class, 1); system.out.println("name:" + c.getname()); tx.commit(); session.close(); }
结果:
hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? name:计算机原理 name:计算机原理
虽然关闭了session,但是二级缓存仍然存在,所以只生成了一次sql语句。
下面的例子:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); query query = session.createquery("from course"); iterator iter = query.iterate(); while(iter.hasnext()){ system.out.println(((course)iter.next()).getname()); } tx.commit(); session.close(); session = sessionfactory.opensession(); tx = session.begintransaction(); query = session.createquery("from course"); iter = query.iterate(); while(iter.hasnext()){ system.out.println(((course)iter.next()).getname()); } tx.commit(); session.close(); }
结果:
hibernate: select course0_.id as col_0_0_ from clas course0_ hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 计算机原理 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 计算机网络 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 数据库原理 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? c语言 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 大学英语a hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? java hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? linux hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 高等数学 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 语文 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 大学物理 hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 软件工程 hibernate: select course0_.id as col_0_0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程
当使用query的list()方法时,只生成一次sql语句查询出所有的对象,使用iterate()方法时,会先得到所有对象的id,然后根据每个id生成一次sql语句查询。第二个session里面使用的也是iterate()方法,首先生成一次sql语句,得到id,然后根据id查找对象,由于开启了二级缓存,在二级缓存里面找到了对象,所以就直接输出了,并没有再根据每个id生成sql语句。
不论是一级缓存还是二级缓存,都只能缓存对象,不能缓存属性的值。下面的例子:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); query query = session.createquery("select c.name from course c"); list<string> names = query.list(); for(iterator iter = names.iterator(); iter.hasnext();){ string name = (string) iter.next(); system.out.println(name); } system.out.println("----------"); query = session.createquery("select c.name from course c"); names = query.list(); for(iterator iter = names.iterator(); iter.hasnext();){ string name = (string) iter.next(); system.out.println(name); } system.out.println("----------"); tx.commit(); session.close(); }
运行结果:
hibernate: select course0_.name as col_0_0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ---------- hibernate: select course0_.name as col_0_0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ----------
虽然开启了二级缓存,但是查询的结果不是对象,是属性,所以并没有缓存,第2次查询仍然生成了查询语句。要解决这个问题,就需要查询缓存。
3. 查询缓存
在配置了二级缓存的基础上,可以设置查询缓存,在sessionfactory的设置里面加上一行:
<prop key="hibernate.cache.use_query_cache">true</prop>
即打开了查询缓存。查询缓存也是sessionfactory级别的缓存,在整个sessionfactory里面都是有效的。
关闭二级缓存,运行下面的例子,在query后面添加setcacheable(true)打开查询缓存:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); query query = session.createquery("select c.name from course c"); query.setcacheable(true); list<string> names = query.list(); for(iterator iter = names.iterator(); iter.hasnext();){ string name = (string) iter.next(); system.out.println(name); } system.out.println("----------"); query = session.createquery("select c.name from course c"); query.setcacheable(true); names = query.list(); for(iterator iter = names.iterator(); iter.hasnext();){ string name = (string) iter.next(); system.out.println(name); } system.out.println("----------"); tx.commit(); session.close(); }
结果:
hibernate: select course0_.name as col_0_0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ---------- 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ----------
由于两次查询的hql语句是一致的,所以只生成一次sql语句。但是如果把第二次查询改一下:
system.out.println("----------"); query = session.createquery("select c.name from course c where c.id > 5"); query.setcacheable(true); names = query.list(); for(iterator iter = names.iterator(); iter.hasnext();){ string name = (string) iter.next(); system.out.println(name); } system.out.println("----------");
结果:
hibernate: select course0_.name as col_0_0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ---------- hibernate: select course0_.name as col_0_0_ from clas course0_ where course0_.id>5 大学英语a java linux 高等数学 语文 大学物理 软件工程 ----------
由于hql语句变了,所以第二次也生成了sql语句。
查询缓存可以缓存属性,也可以缓存对象,但是当缓存对象时,只缓存对象的id,不会缓存整个对象。下面的例子:
@override public void testcache() { // todo auto-generated method stub session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); query query = session.createquery("from course"); query.setcacheable(true); list<course> list = query.list(); for (int i=0; i<list.size(); i++){ system.out.println(list.get(i).getname()); } system.out.println("----------"); tx.commit(); session.close(); session = sessionfactory.opensession(); tx = session.begintransaction(); query = session.createquery("from course"); query.setcacheable(true); list = query.list(); for (int i=0; i<list.size(); i++){ system.out.println(list.get(i).getname()); } system.out.println("----------"); tx.commit(); session.close(); }
结果:
hibernate: select course0_.id as id0_, course0_.name as name0_, course0_.comment as comment0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ---------- hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? hibernate: select course0_.id as id0_0_, course0_.name as name0_0_, course0_.comment as comment0_0_ from clas course0_ where course0_.id=? 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ----------
由于开了查询缓存,没有开二级缓存,虽然使用的是list()方法一次查询出了所有的对象,但是查询缓存只缓存了对象id,没有缓存整个对象。所以在第2个session里面"from course"这个hql由于和前面的相同,并没有生成sql语句,但是由于没有开二级缓存,没有缓存整个对象,只能根据每个id去生成一次sql语句。虽然两次用的都是list()方法,但是第一次是生成sql语句去一次查询出所有的对象,而第二次是根据查询缓存里面的id一个一个的生成sql语句。
如果同时打开查询缓存和二级缓存,第2个session里面就不用再根据id去生成sql语句了:
hibernate: select course0_.id as id0_, course0_.name as name0_, course0_.comment as comment0_ from clas course0_ 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ---------- 计算机原理 计算机网络 数据库原理 c语言 大学英语a java linux 高等数学 语文 大学物理 软件工程 ----------
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!