Hibernate上路_21-二级缓存

把常用数据库持久化对象,作为缓存保存在内存中,减少与数据库交互次数,可以提高性能。
一级缓存:session级别的缓存,属于事务范围。hibernate框架内置。
二级缓存:sessionFactory级别的缓存,属于进程范围的缓存。需引入外部缓存插件并配置。

二级缓存提供商

    1EHCache
    2OSCache
    3Swarm Cache
    4JBOSS Cache 

EHCache
OSCache主要用于单机数据库软件,SwarmCacheJBossCache主要用于集群环境。

二级缓存四个部分组成
    1》类级别缓存区
    2》集合级别的缓存区
    3》更新时间戳
    4》查询缓存(三级缓存)

二级缓存并发策略,与事务隔离级别相对应:
    1》非严格读写(nonstrict-read-write) -------- read uncommitted  
    2》读写型(read-write) ----------------------- read committed  
    3》事务型(transactional) -------------------- repeatable read 
    4》只读型(read-only) ------------------------ serializable 

数据库中极少被改动的数据,比较适用于放入二级缓存。

本文使用EHCache进行Hibernate框架二级缓存。

1.配置使用EHCache:

    1)添加jar包:

解压hibernate后从lib/optional/ehcache得到

    ehcache-core-2.4.3.jar
     hibernate-ehcache-4.2.7.SP1.jar
    slf4j-api-1.6.1.jar


    2)配置hibernate.cfg.xml:

        (1)开启二级缓存:

<hibernate-configuration>
<session-factory>
	<!-- 开启二级缓存 -->
	<property name="hibernate.cache.use_second_level_cache">true</property>


        (2)配置hibernate.cfg.xml 缓存的供应商:

<hibernate-configuration>
<session-factory>
    <!-- 开启二级缓存 -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <!-- hibernate3配置:指明缓存供应商 -->
  <!-- 
      <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
      </property>
  -->
    <!-- hibernate4配置:指明缓存供应商 -->
  <property name="hibernate.cache.region.factory_class">
        org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>


        (3)指定对哪些数据应用二级缓存:

    有两种配置方式POJO.hbm.xml配置,和hibernate.cfg.xml配置。
    例:在hibernate.cfg.xml配置,<class-cache>类级别缓存;<collection-cache>集合级别缓存

<hibernate-configuration>
  <session-factory>
    <!-- 开启二级缓存 -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <!-- 指明缓存供应商 -->
  <property name="hibernate.cache.region.factory_class">
      org.hibernate.cache.ehcache.EhCacheRegionFactory
  </property> 
  
  <!-- property、mapping等配置在中间 -->
    
    <!-- 类级别配置:指明使用二级缓存的类: 
        usage=二级缓存并非策略
        class=对应的POJO     -->
    <class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoUser"/>
    <class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoOrder"/>
		
    <!-- 集合级别配置:指明使用二级缓存的集合: 
        usage=二级缓存并非策略
        collection=对应POJO中的集合变量     -->
    <collection-cache usage="read-write" collection="cn.cvu.hibernate.domain.PojoUser.orders"/>
		
	</session-factory>
</hibernate-configuration>


    3)建立ehcache.xml:

在src下创建,主要用来配置EHCache插件的属性。可以将hibernate解压的/project/etc/ehcache.xml复制到src下进行修改。


    4)测试:

        (1)开启状态:

public void testCache(){
		//二级缓存是SessionFactory级别的,可以被多个Session共享。
		/*** 第一个Session **/
		Session session1 = UtilGetSession.openSession();
		Transaction transaction1 = session1.beginTransaction();
		//存入一级缓存和二级缓存
		PojoUser user1 = (PojoUser) session1.get(PojoUser.class, 1);
		System.out.println(user1.hashCode());
		//直接使用一级缓存中存在的数据
		PojoUser user2 = (PojoUser) session1.get(PojoUser.class, 1);
		System.out.println(user2.hashCode());
		transaction1.commit();
		session1.close();
		
		/*** 第二个Session,不产生新的sql语句 **/
		Session session2 = UtilGetSession.openSession();
		Transaction transaction2 = session2.beginTransaction();
		//使用二级缓存中的数据在新的一级缓存封装数据
		PojoUser user3 = (PojoUser) session2.get(PojoUser.class, 1);
		System.out.println(user3.hashCode());
		//使用新一级缓存中的数据
		PojoUser user4 = (PojoUser) session2.get(PojoUser.class, 1);
		System.out.println(user4.hashCode());
		transaction2.commit();
		session2.close();
		
		/** 以上操作仅执行一条sql **/
	}


        (2)关闭状态:


2.二级缓存存储原理:

    1)类级别缓存区:

        (1)散装数据的存储:每次使用二级缓存,将获得一个新的对象:


        (2)Query接口可以将数据放置到类级别的二级缓存中,但是不能使用query接口的list方法从缓存中获取数据,能存不能取:

@Test
	public void testCache(){
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();
		//Query接口可以将数据放置到类级别的二级缓存中,但是不能使用list方法从缓存中获取数据
		Query query1 = session.createQuery("from PojoUser");
		//Query可以向二级缓存存入数据
		List<PojoUser> list1 = query1.list();	//生成第一条sql
		System.out.println(list1);
		transaction.commit();
		session.close();
		
		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();
		//直接从二级缓存获取,无sql生成
		PojoUser user = (PojoUser) session.get(PojoUser.class, 2);
		System.out.println("直接读取二级缓存:" + user);
		transaction.commit();
		session.close();
		
		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();
		Query query2 = session.createQuery("from PojoUser");
		//Query不能从二级缓存读取数据
		List<PojoUser> list2 = query2.list();	//因为不能读,生成第2条sql
		System.out.println(list2);
		transaction.commit();
		session.close();
	}


    2)集合级别缓冲区:

        (1)测试集合缓存:

public void testCacheList() {
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();

		PojoUser user1 = (PojoUser) session.get(PojoUser.class, 1);
		System.out.println("第1次查询集合:" + user1.getOrders().size()); // 存入二级缓存

		transaction.commit();
		session.close();

		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();

		PojoUser user2 = (PojoUser) session.get(PojoUser.class, 1);
		System.out.println("第2次查询集合:" + user2.getOrders().size()); // 从二级缓存取数据
		for (PojoOrder order : user2.getOrders()) {	// 从二级缓存取数据
			System.out.println("订单:" + order.getName());
		}

		transaction.commit();
		session.close();
	}


        (2)注释 <collection-cache/> 测试:

<hibernate-configuration>
	<session-factory>
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<property name="hibernate.cache.region.factory_class">
  org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  
      <!-- property、mapping等配置在中间 -->
  
		<class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoUser"/>
		<class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoOrder"/>
  <!-- 注释掉集合配置 --> 
<!-- 		<collection-cache usage="read-write" 
  collection="cn.cvu.hibernate.domain.PojoUser.orders"/> -->
		
	</session-factory>
</hibernate-configuration>


        (3)注释<class-cache class="PojoOrder"/>测试:

<hibernate-configuration>
	<session-factory>
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<property name="hibernate.cache.region.factory_class">
  org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  <!-- property、mapping等配置在中间 -->
		<class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoUser"/>
  
		<!-- 注释集合所保存的数据类
  <class-cache usage="read-write" class="cn.cvu.hibernate.domain.PojoOrder"/> -->
  
  <collection-cache usage="read-write" 
  collection="cn.cvu.hibernate.domain.PojoUser.orders"/> 
		
	</session-factory>
</hibernate-configuration>


        (4)集合级别缓存存放的是保存在类级别缓冲区中的数据对象的OID:


    3)一级缓存同步:

当一级缓存中的数据发生更新,会自动同步到二级缓存。

public void testCacheList() {
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();

		PojoUser user1 = (PojoUser) session.get(PojoUser.class, 1);
		System.out.println("第1次:" + user1.getName()); // 存入一级缓存和二级缓存
		user1.setName("二级缓存!");//从一级缓存更新到二级缓存,也更新到数据库

		transaction.commit();
		session.close();

		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();

		PojoUser user2 = (PojoUser) session.get(PojoUser.class, 1);
		System.out.println("第2次:" + user2.getName()); // 从二级缓存取数据

		transaction.commit();
		session.close();
	}


3.将二级缓存数据保存到硬盘:

配置ehcache.xml文件:

    1)保存路径:


    2)默认缓存配置:

默认配置对全局有效。如需要可单独配置对应POJO的<cache>,<cache name="cn.itcast.domain.POJO" name="对应POJO" 其它属性...> 。


    3)测试:

在hibernate.cfg.xml中缓存全部POJO,查询大量数据(超过defaultCache的maxElementsInMemory,并且overflowToDisk为true),在diskStore配置的目录查看是否有缓存文件。


4.更新时间戳区域:

相关:org.hibernate.cache.spi.UpdateTimestampsCache 

作用:保证二级缓存数据有效性。

情景:事务1查询了数据,保存到二级缓存,当事务1再次使用本线程的二级缓存前,有事务2更新了数据库,导致事务1的二级缓存数据是无效的。更新时间戳在二级缓存开辟一个区域,保存每次读写的时间,当执行查询时先对比时间戳区内的版本,如果大于类缓存区中的版本,就重新查询数据库。

public void testTimer(){
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();
		
		//存入一级缓存和二级缓存
		PojoUser user1 = (PojoUser) session.get(PojoUser.class, 2);	//创建时间戳t1
		System.out.println(user1);
		
		//直接修改数据库,模拟多用户访问
		session.createQuery("update PojoUser set name='猫王' where id = 2").executeUpdate();	//创建时间戳t2

		transaction.commit();
		session.close();
		
		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();
		
		//读取二级缓存,首先检查时间戳,如果有异,重新查询数据库
		PojoUser user2 = (PojoUser) session.get(PojoUser.class, 2);	//判断t2大于t1
		System.out.println(user2);
		
		transaction.commit();
		session.close();
	}


注意:hibernate能感知的操作才会创建时间戳。


5.Query的iterate方法:

list方法只能写,不能读二级缓存。

Query的iterate方法返回List集合的迭代器对象Iterator。使用Query的iterate方法遍历时,集合中保存的是只有OID的代理对象,当访问对象的其它属性时,才进行初始化。初始化时,优先查找二级缓存;如果缓存中不存在,则生成SQL语句查询数据库。

当使用二级缓存时,iterate比list 效率要好。

public void testTimer(){
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();
		
		//3条记录存入二级缓存
		session.createQuery("from PojoOrder where id <= 3").list();
		
		transaction.commit();
		session.close();
		
		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();
		
		//读取二级缓存,获得3条,剩余2条执行sql
		Iterator iterate = session.createQuery("from PojoOrder where id <= 5").iterate();
		while (iterate.hasNext()) {
			PojoOrder order = (PojoOrder) iterate.next();
			System.out.println(order.getName());
		}
		
		transaction.commit();
		session.close();
	}


6.查询缓存:

之前类级别缓存、集合级别缓存,都是将数据存入类级别缓冲区,通过id查询对象。key是对象的id,value是缓存对象;这里的查询缓存key是HQL语句或SQL语句,value是查询结果数据。查询缓存依赖二级缓存。

    1)启用查询缓存:

        在hibernate.cfg.xml中添加:

<hibernate-configuration>
	<session-factory>
		<!-- 启用查询缓存 -->
		<property name= "hibernate.cache.use_query_cache">true</property>


    2)写入和读取都setCacheable为true :

public void testTimer(){
		Session session = UtilGetSession.openSession();
		Transaction transaction = session.beginTransaction();
		//表中的一列,数据对象的部分属性 无法缓存,之后的操作无法使用。设置cacheAble为true即可
		List list1 = session.createQuery("select name from PojoUser").setCacheable(true).list();
		System.out.println(list1);
		transaction.commit();
		session.close();
		
		session = UtilGetSession.openSession();
		transaction = session.beginTransaction();
		//要想从缓存读取,设置cacheAble为true即可
		List list2 = session.createQuery("select name from PojoUser").setCacheable(true).list();
		System.out.println(list2);
		transaction.commit();
		session.close();
	}



- end
  

转载于:https://my.oschina.net/vigiles/blog/178093

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值