Hibernate的二级缓存

来源:互联网 发布:数据库王珊第5版答案 编辑:程序博客网 时间:2024/06/07 06:32
一、缓存(Cache):计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存或者硬盘
二、Hibernate中提供了两个级别的缓存
       1、第一级别的缓存是 Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的
       2、第二级别的缓存是 SessionFactory级别的缓存,它是属于进程范围的缓存

注意本篇的测试环境使用--HIbernate之HQL所搭建的环境

一级缓存:

缓存范围:缓存只能被当前Session对象访问。缓存的生命周期依赖于Session的生命周期,当Session被关闭后,缓存也就结束生命周期。
@Testpublic void testHibernateFirstLevelCache(){Employee emp=(Employee) session.get(Employee.class, 8);System.out.println(emp.getName());Employee emp1=(Employee) session.get(Employee.class, 8);System.out.println(emp1.getName());}
控制台信息:
Hibernate:     select        employee0_.ID as ID1_1_0_,        employee0_.NAME as NAME2_1_0_,        employee0_.SALARY as SALARY3_1_0_,        employee0_.EMAIL as EMAIL4_1_0_,        employee0_.DEPT_ID as DEPT_ID5_1_0_     from        EMPLOYEE employee0_     where        employee0_.ID=?name6name6
在执行第二个查询的时候并没有发出SQL语句到数据库查询,而是直接从session缓存中读取。

Hibernate一些与一级缓存相关的操作(时间点):

数据放入缓存:

1. save()。当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。

2. get()load()。当session对象调用get()load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。

3. 使用HQLQBC等从数据库中查询数据。

二级缓存:

1.SessionFactory级别的缓存,可以跨越Session存在,可以被多个Session所共享
2.SessionFactory的缓存可以分为两类:
  –内置缓存: Hibernate 自带的, 不可卸载.通常在Hibernate的初始化阶段,Hibernate 会把映射元数据和预定义的 SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中数据(.hbm.xml文件中的数据)的复制.该内置缓存是只读的.
  –外置缓存(二级缓存):一个可配置的缓存插件.在默认情况下,SessionFactory不会启用这个缓存插件.外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘

3.适合放到二级缓存中:

1)经常被访问

2)改动不大

3)数量有限

4不是很重要的数据,允许出现偶尔并发的数据。 

这样的数据非常适合放到二级缓存中的。

4. 二级缓存的并发访问策略:
-两个并发的事务同时访问持久层的缓存的相同数据时,也有可能出现各类并发问题.
-二级缓存可以设定以下 4 种类型的并发访问策略,每一种访问策略对应一种事务隔离级别:
–非严格读写(Nonstrict-read-write):不保证缓存与数据库中数据的一致性. 提供Read Uncommited事务隔离级别,对于极少被修改,而且允许脏读的数据,可以采用这种策略
读写型(Read-write):提供ReadCommited数据隔离级别.对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读
–事务型(Transactional):仅在受管理环境下适用.它提供了 RepeatableRead事务隔离级别. 对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读
–只读型(Read-Only):提供 Serializable数据隔离级别,对于从来不会被修改的数据,可以采用这种访问策略

使用Hibernate的二级缓存:

 1.选择合适的缓存插件: EHCache(jar包和配置文件), 并编译器配置文件
    a、将hibernate\hibernate-release-4.2.5.Final\hibernate-release-4.2.5.Final\lib\optional\ehcache路径下的所有jar包加入到工程的lib目录,并BuildPath!
          ehcache-core-2.4.3,jar
          hibernate-ehcache-4.2.5.Final.jar
          slf4j-api-1.6.1.jar
    b、将hibernate\hibernate-release-4.2.5.Final\hibernate-release-4.2.5.Final\project\etc路径下的ehcache.xml配置文件加入到工程的类路径下
 2. 修改Hibernate的配置文件:hibernate.cfg.xml  ,加入如下配置:
     1、 开启二级缓存:    
<!-- 启用二级缓存 -->       <property name="cache.use_second_level_cache">true</property> 
     2、指定使用的二级缓存产品:
<!-- 配置使用的二级缓存产品 -->       <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    3、指定使用二级缓存的持久化类
<!-- 对那个持久化类启用二级缓存 -->       <class-cache usage="read-write" class="com.elgin.hibernate.entity.Employee"/>
配置这一步的时候需要注意:class-cache元素最好放到mapping元素的下面,否则会报xml解析错误:元素类型为 "session-factory" 的内容必须匹配 "(property*,mapping*,(class-cache|collection-cache)*,event*,listener*)"
实际上这个配置也可以配置在持久化类对应的hbm配置文件中。
*******************************二级缓存测试开始**************************
未使用二级缓存的测试:
   @Testpublic void testHibernateSecondLevelCache(){Employee emp=(Employee) session.get(Employee.class, 8);System.out.println(emp.getName());transcation.commit();session.close();session=sessionFactory.openSession();transcation=session.beginTransaction();Employee emp1=(Employee) session.get(Employee.class, 8);System.out.println(emp1.getName());}
运行之后,控制台显示:
Hibernate:     select        employee0_.ID as ID1_1_0_,        employee0_.NAME as NAME2_1_0_,        employee0_.SALARY as SALARY3_1_0_,        employee0_.EMAIL as EMAIL4_1_0_,        employee0_.DEPT_ID as DEPT_ID5_1_0_     from        EMPLOYEE employee0_     where        employee0_.ID=?name6Hibernate:     select        employee0_.ID as ID1_1_0_,        employee0_.NAME as NAME2_1_0_,        employee0_.SALARY as SALARY3_1_0_,        employee0_.EMAIL as EMAIL4_1_0_,        employee0_.DEPT_ID as DEPT_ID5_1_0_     from        EMPLOYEE employee0_     where        employee0_.ID=?name6
通过控制台结果可以看出:由于第二次查询之前使用了新的session,缓存中无数据,并且未开启二级缓存,所以重新发出了SQL语句进行了查询。
使用二级缓存的测试:
   @Testpublic void testHibernateSecondLevelCache(){Employee emp=(Employee) session.get(Employee.class, 8);System.out.println(emp.getName());transcation.commit();session.close();session=sessionFactory.openSession();transcation=session.beginTransaction();Employee emp1=(Employee) session.get(Employee.class, 8);System.out.println(emp1.getName());}
控制台打印显示:
Hibernate:     select        employee0_.ID as ID1_1_0_,        employee0_.NAME as NAME2_1_0_,        employee0_.SALARY as SALARY3_1_0_,        employee0_.EMAIL as EMAIL4_1_0_,        employee0_.DEPT_ID as DEPT_ID5_1_0_     from        EMPLOYEE employee0_     where        employee0_.ID=?name6name6
通过与未使用的结果对比发现,第二次并没有发出SQL语句,而是直接使用,这就是Hibernate的二级缓存起作用了!

Hibernate集合级别的二级缓存配置:

1. 在上述hibernate配置文件的基础上新增如下配置:    
<class-cache usage="read-write" class="com.elgin.hibernate.entity.Department"/><collection-cache usage="read-write" collection="com.elgin.hibernate.entity.Department.emps"/>
很明显上面的一句是开启持久化类Department的二级缓存,下面是开启Department下名为emps的集合属性开启二级缓存
注意:开启集合属性二级缓存的同时,一定要为集合中保存的持久化类开启二级缓存(本例中为Employee类,上述配置已开启,直接测试)
@Testpublic void testCollectionSecondLevelCache(){Department dept=(Department) session.get(Department.class, 6);System.out.println(dept.getName());System.out.println(dept.getEmps().size());transcation.commit();session.close();session=sessionFactory.openSession();transcation=session.beginTransaction();Department dept1=(Department) session.get(Department.class, 6);System.out.println(dept1.getName());System.out.println(dept1.getEmps().size());}
控制台信息:
Hibernate:     select        department0_.ID as ID1_0_0_,        department0_.NAME as NAME2_0_0_     from        DEPARTMENT department0_     where        department0_.ID=?测试部Hibernate:     select        emps0_.DEPT_ID as DEPT_ID5_0_1_,        emps0_.ID as ID1_1_1_,        emps0_.ID as ID1_1_0_,        emps0_.NAME as NAME2_1_0_,        emps0_.SALARY as SALARY3_1_0_,        emps0_.EMAIL as EMAIL4_1_0_,        emps0_.DEPT_ID as DEPT_ID5_1_0_     from        EMPLOYEE emps0_     where        emps0_.DEPT_ID=?2测试部2
从上述信息可以看出先查询dept,并且dept中的emps属性使用了延迟加载。在使用新的session查询时,并没有发出SQL语句,而是直接使用,这就是配置的集合缓存的作用!

二级缓存配置文件ehcache.xml的配置

<ehcache>    <!-- Sets the path to the directory where cache .data files are created.         If the path is a Java System Property it is replaced by         its value in the running VM.         The following properties are translated:         user.home - User's home directory         user.dir - User's current working directory         java.io.tmpdir - Default temp file path          指定一个目录,当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.     -->    <diskStore path="java.io.tmpdir"/>
    <!--Default Cache configuration. These will applied to caches programmatically created through        the CacheManager.        The following attributes are required for defaultCache:        maxInMemory       - Sets the maximum number of objects that will be created in memory        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element                            is never expired.        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used                            if the element is not eternal. Idle time is now - last accessed time        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used                            if the element is not eternal. TTL is now - creation time        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache                            has reached the maxInMemory limit.         设置缓存的默认数据过期策略         -->    <defaultCache        maxElementsInMemory="10000"        eternal="false"        timeToIdleSeconds="120"        timeToLiveSeconds="120"        overflowToDisk="true"        />    <!--                 设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域                缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。                                如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>                Hibernate在不同的缓存区域保存不同的类/集合。                             对于类而言,区域的名称是类名。如:com.elgin.hibernate.entity.Employee                             对于集合而言,区域的名称是类名加属性名。如com.elgin.hibernate.entity.Department.emps      -->     <!--                 name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字   maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目   eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false   timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。                  如果此值为0,表示对象可以无限期地处于空闲状态。   timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中.                  该属性值必须大于或等于 timeToIdleSeconds 属性值   overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中       -->    <cache name="com.elgin.hibernate.entity.Employee"        maxElementsInMemory="10000"        eternal="false"        timeToIdleSeconds="300"        timeToLiveSeconds="600"        overflowToDisk="true"        />    <cache name="com.elgin.hibernate.entity.Department.emps"        maxElementsInMemory="1000"        eternal="true"        timeToIdleSeconds="0"        timeToLiveSeconds="0"        overflowToDisk="false"        /> </ehcache>

Hibernate二级缓存的查询缓存

1. 默认情况下,设置的二级缓存对HQL查询和QBC查询是无效的, 可以通过如下设置使其生效:
    a、在hibernate的配置文件hibernate.cfg.xml中增加如下配置,声明开启查询缓存
<property name="cache.use_query_cache">true</property>
   b、在代码中 调用Query或者Criteria的setCacheable(true)方法
2. 查询缓存依赖于二级缓存,如果想要使用,必须先配置Hibernate的二级缓存
测试代码:
@Testpublic void testQueryCache(){String hql="from Employee";Query query=session.createQuery(hql);query.setCacheable(true);List<Employee> emps=query.list();System.out.println(emps.size());List<Employee> emps1=query.list();System.out.println(emps1.size());}
控制台显示:
Hibernate:     select        employee0_.ID as ID1_1_,        employee0_.NAME as NAME2_1_,        employee0_.SALARY as SALARY3_1_,        employee0_.EMAIL as EMAIL4_1_,        employee0_.DEPT_ID as DEPT_ID5_1_     from        EMPLOYEE employee0_2020
可以看出,第二次查询并没有发出SQL语句,说明第一次的结果被缓存了。










   


0 0
原创粉丝点击