Hibernate学习笔记(六)——缓存

来源:互联网 发布:sem优化 编辑:程序博客网 时间:2024/06/07 07:15

一、Hibernate缓存

1、什么是缓存?

Hibernate中用到了缓存的概念,那么什么是缓存呢?这里介绍的缓存并不是指计算机的内存或者CPU的一二级缓存,这里的缓存是指为了降低程序对物理数据源访问的频次,从而提高程序运行性能的一种策略。

2、为什么要使用缓存?

为什么Hibernate要使用缓存呢?可以从三点进行分析:(1)Hibernate是一个ORM框架,ORM框架访问数据库的效率直接影响应用程序的运行速度,提升和优化ORM框架的执行效率至关重要;(2)Hibernate的缓存是提升和优化Hibernate执行效率的重要手段,所以学会Hibernate缓存的使用和配置是优化的关键;(3)评判一个ORM框架是否优秀,访问数据库的频次就一个重要的标准

3、缓存使如何工作的?

Hibernate缓存就是将数据库中的数据存储在内存中,缓存的一般工作原理可以通过下图来进行分析(图片摘自慕课网)。

应用程序要查询某一条数据时首先会去缓存中查找,如果在缓存中找到相应的数据,则为缓存命中,将命中的数据直接从缓存中取出返回给应用程序,如果未命中则去数据库查询相应的数据,将数据库中的数据返回给应用程序,同时也会在缓存中保存数据以备下次查询。
在Hibernate中用到了两种缓存,分别是一级缓存和二级缓存,下面将分别介绍这两种缓存的基本内容,本次介绍的案例涉及到一张员工表employee,表中有两个字段empid和empname,我们预先在表中保存两条数据。

二、一级缓存

首先我们来看下面这个例子。
@Testpublic void testCache() {Session session = getSession();Employee emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());}
我们使用JUnit来进行测试,其中getSession()方法是一个获取Session对象的工具方法,在这个例子中我们首先获取Session,然后查询empid为1的对象,接着再做一次查询,仍然查询empid为1的对象,我们运行测试方法,查看控制台的输出。
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?张三张三
可以看到控制台只打印了一次查询的SQL,这说明用Session第一次查询到empid为1的对象之后这个对象被缓存起来了,第二次查询的时候直接从缓存中获取相应的对象,而不是再查一次数据库。这个例子表明Hibernate的缓存是与Session有关的。
我们修改上面的例子再来看一下。
@Testpublic void testCache() {Session session = getSession();Employee emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());session = getSession();emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());}
在这个例子中我们使用了两个Session来查询同一个对象,运行程序,查看控制台输出。
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?张三Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?张三
我们发现当我们使用不同的Session查询同一个对象时,控制台打印了两次查询SQL,与上面一个例子结合起来我们发现,同一个Session第二次访问同一个对象将使用缓存,而在不同的Session中多次查询同一个对象时,会执行多次数据库查询。
上面两个例子就简单介绍了一下Hibernate的一级缓存,Hibernate的一级缓存又称为“Session缓存”、“会话级缓存”;在通过Session从数据库查询实体时会把实体在内存中存储起来,下一次查询同一实体时将不再从数据库中获取,而是从内存中获取,这就是缓存;一级缓存的生命周期和Session相同,Session销毁时,缓存也跟着销毁了;一级缓存的数据可适用范围只在当前的会话内,所以不同的Session多次查询同一个对象会执行多次的数据库查询;在一级缓存当中,持久化类的每个实例都具有唯一的OID,也就是说同一个Session两次查询同一个对象时,第二次是不会再将对象保存在缓存当中的。
一级缓存时无法取消的,是Hibernate的默认缓存,但可以通过两个方法进行管理,分别为:evict(),用于将某个对象从Session的一级缓存中清除;clear():用于将一级缓存中的所有对象全部清除。看一下下面的例子,我们使用evict()将第一次查询到的Employee对象移除缓存,这样在第二次查询时将再一次从数据库中获取,也就是说控制台将打印两次SQL。
@Testpublic void testCache() {Session session = getSession();Employee emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());session.evict(emp);emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());}
使用clear()方法就是将所有的对象都从缓存中移除,这样下次再查询时所有对象都要重新从数据库中查找。
接下来我们来看另一个例子,我们使用Query.list()方法来获取所有的员工信息,查询一次之后我们再次使用Query.list()来进行查询。
@Testpublic void testCache() {Session session = getSession();Query query = session.createQuery("from Employee");List<Employee> list = query.list();for(Employee emp : list) {System.out.println(emp.getEmpname());}list = query.list();for(Employee emp : list) {System.out.println(emp.getEmpname());}}
运行程序,观察控制台输出,控制台打印如下信息。
Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_张三李四Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_张三李四
我们发现打印了两次SQL,这说明Query.list()方法不会使用缓存,也就是说每次查询时都要从数据库中重新查询数据。如果我们使用Query.iterate()方法会怎样呢?
@Testpublic void testCache() {Session session = getSession();Query query = session.createQuery("from Employee");List<Employee> list = query.list();for(Employee emp : list) {System.out.println(emp.getEmpname());}Iterator<Employee> it = query.iterate();while(it.hasNext()) {System.out.println(it.next().getEmpname());}}
运行程序观察控制台输出。
Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_张三李四Hibernate: select employee0_.empid as col_0_0_ from employee employee0_张三李四
我们发现在使用Query,list()方法查询到所有数据之后,在使用Query.iterate()方法时只是去查询了empid的信息,我们再次修改上面的例子,只使用Query.iterate()进行查询。
@Testpublic void testCache() {Session session = getSession();Query query = session.createQuery("from Employee");Iterator<Employee> it = query.iterate();while(it.hasNext()) {System.out.println(it.next().getEmpname());}}
控制台打印如下输出:
Hibernate: select employee0_.empid as col_0_0_ from employee employee0_Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?张三Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?李四
Hibernate首先去数据库查询empid信息,然后又根据empid信息去数据库中查询了相应的数据。结合上面的例子我们其实可以发现Query.iterate()其实是用到缓存了的,它首先将所有对象的id查询出来,然后根据id到缓存中将所有对象都查询出来,如果缓存中没有数据,则把对象从数据库中一条一条的查出来。

三、二级缓存

介绍完一级缓存之后我们接着来看一下二级缓存,与一级缓存是默认的缓存不同,Hibernate的二级缓存不是默认的,它不由框架包含而是由第三方组件提供,是一个可插拔式缓存,Hibernate默认提供了一种缓存组件EHCache,同时还支持其他的二级缓存组件,例如Hashtable、OSCache等;二级缓存又称为“全局缓存”、“应用级缓存”,它的数据可适用范围是当前应用的所有会话,这与一级缓存只适用于当前会话内不同。下面我们来看看EHCache的使用。
使用EHCache分为下面几个步骤:1、引入对应的jar包;2、在hibernate.cfg.xml文件中添加相应的配置;3、添加ehcache.xml配置文件;4、在需要缓存的表对应的映射文件中添加<cache/>标签。
首先我们需要导入相应的jar包,要使用EHCache,需要在pom.xml文件中添加下面两个依赖。
<span style="white-space:pre"></span><dependency>    <groupId>net.sf.ehcache</groupId>    <artifactId>ehcache</artifactId>    <version>2.10.2</version></dependency><dependency>    <groupId>org.hibernate</groupId>    <artifactId>hibernate-ehcache</artifactId>    <version>5.2.3.Final</version></dependency>
接下来我们需要在hibernate.cfg.xml中添加相应的配置,我们需要添加如下配置。
<property name="hibernate.cache.use_second_level_cache">true</property><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property><property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>
在这里需要注意一下Hibernate 4.0及以后的需要按照上面的配置,而Hibernate 3.3 的配置是这样的:
<property name="hibernate.cache.use_second_level_cache">true</property><property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
如果在Hibernate4.0中仍按照Hibernate3.3进行配置的话运行程序会报错
 org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given, please either disable second level cache or set correct region factory class name to property
然后我们需要添加ehcache.xml配置文件,这个配置文件需要放在src目录下。
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">    <diskStore path="java.io.tmpdir"/>     <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/>    <!--         配置自定义缓存        maxElementsInMemory:缓存中允许创建的最大对象数        eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。        timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前,                    两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,                    如果该值是 0 就意味着元素可以停顿无穷长的时间。        timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,                    这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。        overflowToDisk:内存不足时,是否启用磁盘缓存。        memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。    -->    <cache name="Employee"         maxElementsInMemory="10000"         eternal="false"        overflowToDisk="false"         timeToIdleSeconds="900"         timeToLiveSeconds="1800"        memoryStoreEvictionPolicy="LFU" /> </ehcache>
最后我们需要在需要缓存的表对应的映射文件中添加<cache/>标签,在这个例子中我们要对Employee进行缓存,所以我们在Employee.hbm.xml文件中添加如下内容。
<cache usage="read-only" include="all" region="Employee"/>
其中usage属性指定缓存策略,可选的策略包括transactional,read-only,read-write和nonstrict-read-write;region属性指定二级缓存区域名,指定了二级缓存区域名之后就可以在ehcache.xml中配置相应的自定义缓存了;include表示指定是否缓存延迟加载的对象,all表示缓存所有对象,non-lazy表示不缓存延迟加载的对象。
所有都配置完成后我们可以通过下面这个测试方法来进行测试。
@Testpublic void testCache2() {Session session = getSession();Employee emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());session = getSession();emp = session.get(Employee.class, 1);System.out.println(emp.getEmpname());}
运行程序,与一级缓存不同的是当使用不同的Session查询同一对象时不再多次查询数据库,而是直接使用缓存。
那么通常什么类型的数据可以使用到二级缓存呢?通常很少被修改的数据、不是很重要的数据、允许偶尔出现并发的数据和参考数据可以使用二级缓存。

四、缓存小结和对比

通过对缓存的学习我们对Hibernate缓存有了简单的了解,Hibernate缓存能够提高检索效率,而Hibernate缓存又分为一级缓存和二级缓存,其中一级缓存是会话级缓存,而二级缓存是应用级缓存,使用缓存提高检索效率的同时也会对增加服务器的消耗,所以我们需要谨慎使用缓存策略。
通过上面对一级缓存和二级缓存的介绍,我们总结了下面这个表格对两种缓存进行对比(图片摘自慕课网)。
















0 0
原创粉丝点击