hibernate的缓存问题

9/6/2015来源:Java教程人气:2420

hibernate的缓存问题

又到了写总结的时候了,今天来扒拉扒拉hibernate的缓存问题,顺便做了几个小测试,算是学习加上复习吧,下面开始。

1.综述

  首先为什么要有缓存这项技术,详细原因呢我也不知道,唯一知道的一点就是web应用在和数据库打交道时,查询数据库的次数越少越好,可能说的也不太准确吧,什么意思呢,就是尽量减少查询数据库的次数,但是有时候一样的数据我们又需要查询很多次,怎么办呢?这时候就是缓存大展身手的时候了,可以把查询的数据先缓存下来,需要的时候从缓存里面来拿,如果缓存里面没有需要的数据再从数据库去查,这样就可以减少查询数据库的次数,提高应用的查询速度的同时减轻数据库的压力。

2.hibernate缓存

  hibernate的缓存机制大概可以分为一级缓存和二级缓存,有时候还会用到查询缓存,一级缓存是默认开启的,一级缓存是session共享的,因此可以叫做session缓存或者叫做事务缓存,save,update,saveOrUpdate,load,get,list,iterate这些方法都会把对象放在一级缓存中,但是一级缓存不能控制缓存的数量,所以在操作大批量数据时有可能导致内存溢出,可以使用clear,evict方法来清空一级缓存,一级缓存依赖于session,session的生命周期结束了,一级缓存也会跟着结束。

  hibernate的二级缓存是可插拔的,要启用二级缓存需要第三方支持,hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持,可以通过实现CachePRovider和Cache接口加入。

session的save(不适合native方式生成的主键),update,saveOrUpdate,list,iterator,get,load,以及Query, Criteria都会填充二级缓存,但查询缓存时,只有Session的iterator,get,load会从二级缓存中取数据。Query,Criteria由于命中率较低,所以hibernate缺省是关闭的。Query查询命中低的一方面原因就是条件很难保证一致,且数据量大,无法保证数据的真实性。

  hibernate的二级缓存是sessionFactory级别,也就是跨session的,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。

(上述两段引自:http://www.cnblogs.com/shiyangxt/archive/2008/12/30/1365407.html,我自己可能说的没有那么清晰,所以就引用一下大神们的话)

  

3.ehcache实现hibernate二级缓存

  ehcache是用的比较多的一个二级缓存框架,当然还有OSCache等很多二级缓存框架,我暂时就会用ehcache,所以就用ehcache做了个demo,下面详细说一下:

3.1 新建项目

  新建一个java项目,当然建web项目也可以,只是用不到页面而已,测试都是在junit中进行的。给项目添加hibernate支持,然后添加eacache的jar包,因为要和数据库打交道,数据库驱动包也是少不了的,junit的包也是需要的,下面是项目结构截图:

  

  hibernate的版本是4.1版本,环境是myeclipse10.7,jdk是1.7版本,其他的就没有什么了,需要的两个配置文件分别是hibernate的配置文件和ehcache的配置文件,实体类使用了注解的方式。

3.2 配置二级缓存

  如果要使用二级缓存,需要先配置二级缓存,首先在hibernate的配置文件中配置如下信息:

<!-- 开启二级缓存 -->        <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_configuration_file_resource_path">ehcache.xml</property>

关于二级缓存的提供类还有另外一种写法,<property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property>,这种方式是hibernate3的版本使用的,hibernate4版本不推荐使用,但是如果使用的是hibernate3版本的话也可以使用这个。

  开启二级缓存并且加入二级缓存提供类之后就需要ehcache的配置文件了,配置文件详细如下:

<diskStore path="java.io.tmpdir"/>    <defaultCache            maxElementsInMemory="10000"            eternal="true"            overflowToDisk="true"            maxElementsOnDisk="10000000"            diskPersistent="true"            diskExpiryThreadIntervalSeconds="120"            />

  diskStore中的path路径可以设置为硬盘路径,也可以使用如上的设置方式,表示默认存储路径。

  defaultCache为默认的缓存设置,maxElementsInMemory : 在內存中最大緩存的对象数量。

                  eternal : 缓存的对象是否永远不变。

                   timeToIdleSeconds :可以操作对象的时间。

                  timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。

                  overflowToDisk :内存满了,是否要缓存到硬盘。

其他的属性可以在ehcache的core包中的chcache-failsafe.xml文件中找到,我就不一一列举出来了。

  上面的配置是默人配置,如果不指定缓存的对象,那么所有的二级缓存都是使用的默认配置,如果设置了缓存对象,那么则使用置顶的缓存对象配置,置顶缓存对象中没有配置的信息继承默认配置的。

 <cache    name="modal.User"    maxElementsInMemory="200" eternal="false"     timeToIdleSeconds="50"     timeToLiveSeconds="60"     overflowToDisk="true"    />

  上面的就是指定缓存对象的配置,注意name中要把类的包名带上。

上面的工作做完之后就是开启二级缓存了,如果是注解形式的,那么使用如下的方式开启:

@[email protected](name="t_class")@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)@Cacheablepublic class User {    @Id    @GeneratedValue(strategy=GenerationType.IDENTITY)    private int id;    private String name;

  可以设置二级缓存的类型,READ_ONLY表示只读,二级缓存一般设置为只读形式的,因为效率最高,READ_WRITE表示读写,效率低但是可以保证并发正确,nonstrict-read-write:非严格的读写,效率较高,不用加锁,不能保证并发正确性。例如帖子浏览量。transactional:事务性缓存,可回滚缓存数据,一般缓存框架不带有此功能,实现很复杂。

  如果是hbm.xml形式的,那么使用如下的方式开启:

    <cache usage="read-only"/>

3.3 测试

  配置完成了我们来做下测试:

先看下数据库数据,数据库就三条数据:

3.3.1

先做第一个测试,在两个session中加载一条数据,观察控制台的sql语句:

@Test    public void test() {        Session session = null;        try {            session = HibernateSessionFactory.getSession();            User u1 = (User)session.load(User.class, 1);            System.out.println("------------"+u1.getName());        } catch (HibernateException e) {            e.printStackTrace();        } finally {            session.close();        }                try {            session = HibernateSessionFactory.getSession();            User u1 = (User)session.load(User.class, 1);            System.out.println(u1.getName()+"--------");        } catch (HibernateException e) {            e.printStackTrace();        } finally {            session.close();        }            }

sql语句:

Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_class user0_ where user0_.id=?------------301301--------

  通过上面的代码和sql语句以及结果可以看到,在一个sessionFactory的两个session中查询一条记录时,只发出了一条sql语句,说明此时二级缓存是好使的。

下面看一个报错的情况:

@Test    public void test() {        Session session = null;        try {            session = HibernateSessionFactory.getSession();            User u1 = (User)session.load(User.class, 1);            System.out.println("------------"+u1.getName());        } catch (HibernateException e) {            e.printStackTrace();        } finally {            session.close();        }                try {            session = HibernateSessionFactory.getSession();            User u1 = (User)session.load(User.class, 1);            System.out.println(u1.getName()+"--------");                       session.beginTransaction();            u1.setName("222");            session.beginTransaction().commit();        } catch (HibernateException e) {            e.printStackTrace();        } finally {            session.close();        }            }

  看控制台: