Hibernate的二级缓存

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

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

一级缓存:

缓存范围:缓存只能被当前Session对象访问。缓存的生命周期依赖于Session的生命周期,当Session被关闭后,缓存也就结束生命周期。
@Test
	public 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=?
name6
name6
在执行第二个查询的时候并没有发出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): 提供 Read Commited 数据隔离级别.对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读
–事务型(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配置文件中。
*******************************二级缓存测试开始**************************
未使用二级缓存的测试:
   @Test
	public 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=?
name6
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=?
name6
通过控制台结果可以看出:由于第二次查询之前使用了新的session,缓存中无数据,并且未开启二级缓存,所以重新发出了SQL语句进行了查询。
使用二级缓存的测试:
   @Test
	public 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=?
name6
name6
通过与未使用的结果对比发现,第二次并没有发出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类,上述配置已开启,直接测试)
@Test
	public 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的二级缓存
测试代码:
@Test
	public 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_
20
20
可以看出,第二次查询并没有发出SQL语句,说明第一次的结果被缓存了。










   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值