Hibernate缓存机制学习笔记

持久化层的缓存的范围


    缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类:


·事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。


·进程范围:缓存被进程内的所有事务共享。这些事务有可能并发访问缓存,因此必须对缓存采取必要的事务隔离机制。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。


·集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性。

事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存。


Hibernate中的缓存


    Hibernate中提供了两级缓存:第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由Hibernate管理,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或集群范围的缓存。

下面的表格展示了第一级缓存与第二级缓存的区别。


存放数据形式缓存范围并发访问策略数据过期策略物理存储介质
第一级缓存相互关联的持久化对象事务范围每个事务都拥有单独的一级缓存,不会出现并发问题没有提供内存
第二级缓存对象的散装数据进程或者集群范围多个事务会同时访问第二级缓存中相同数据提供数据过期策略。内存和硬盘

一级缓存的管理:

当应用程序调用session的save、update、saveorupdate、get、load以及调用查询接口的list、iterate、filter方法时,如果在session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。

session为应用程序提供了两个管理缓存的方法:

evict(Object obj):从缓存中清除参数指定的持久化对象

clear():清空缓存中所有持久化对象。


    下面是我做过的一个测试session级缓存的例子,Debug的时候可能会看的更清晰一些:

/**
* 在同一个session中发出两次load查询
* fkg
*/
public void testCache1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		Student student = (Student)session.load(Student.class, 1);
		System.out.println("student.name=" + student.getName());
			
		//不会发出查询语句,load使用缓存
		student = (Student)session.load(Student.class, 1);
		System.out.println("student.name=" + student.getName());
			
		session.getTransaction().commit();
	}catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally {
		HibernateUtils.closeSession(session);
	}
}		

二级缓存的管理:

1)条件查询的时候,总是发出sql查询语句到数据库查询,一次获得所有的数据对象

2)把获得的所有数据对象根据ID存放到二级缓存中

3)当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查,查不到,如果配置了二级缓存,那么从二级缓存中查,查不到再查询数据库,把结果按照ID放到缓存。

4)删除、更新、增加数据的时候,同时更新缓存。


二级缓存在实现的时候,需要先在Hibernate的配置文件中设置一下:

<!-- 配置缓存提供商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
		
<!-- 启用二级缓存,这也是它的默认配置 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
		
<mapping resource="com/bjpowernode/hibernate/Student.hbm.xml"/>
<mapping resource="com/bjpowernode/hibernate/Classes.hbm.xml"/>
当然也可以指定某个类使用二级缓存:

<!-- 指定Student使用二级缓存-->
<class-cache class="com.bjpowernode.hibernate.Student" usage="read-only"/>
然后编写一个测试类:

/**
* 开启二级缓存
*
* 在两个session中发load查询
*/
public void testCache1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		Student student = (Student)session.load(Student.class, 1);
		System.out.println("student.name=" + student.getName());
		session.getTransaction().commit();
	}catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally {
		HibernateUtils.closeSession(session);
	}
		
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		Student student = (Student)session.load(Student.class, 1);
			
		//不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据
		//二级缓存是进程级的缓存
		System.out.println("student.name=" + student.getName());
		session.getTransaction().commit();
	}catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally {
		HibernateUtils.closeSession(session);
	}
		
}		

·什么样的数据适合放到第二级缓存

1)很少被修改的数据

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

3)不会被并发访问的数据

4)参考数据。


查询缓存

    对于经常适用的查询语句,如果启用了查询缓存,当第一次执行查询语句时,Hibernate会把查询结果放到第二缓存中。以后再次执行该查询语句时,只需从缓存中获得查询结果,从而提高查询性能。

下面是我做的一个小实例:

1)引入ehcache.xml文件放到src文件夹下。

2)编写实体类等

3)测试类:

/**
* 开启查询,关闭二级缓存,采用query.list()查询普通属性
* 
* 在一个session中发query.list()查询
*/
public void testCache1() {
	Session session = null;
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		List names = session.createQuery("select s.name from Student s")
				.setCacheable(true)
				.list();
		for (int i=0; i<names.size(); i++) {
			String name = (String)names.get(i);
			System.out.println(name);
		}
		System.out.println("-------------------------------------------------------");
		//不会发出查询语句,因为启用查询缓存
		names = session.createQuery("select s.name from Student s")
				.setCacheable(true)
				.list();
		for (int i=0; i<names.size(); i++) {
			String name = (String)names.get(i);
			System.out.println(name);
		}
		session.getTransaction().commit();
	}catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally {
		HibernateUtils.closeSession(session);
	}
}


学习小结

    一级缓存是必要的,需要根据实际的业务需要考虑要不要使用二级缓存。对于查询缓存还是第一次以这种形式学习到,希望在实践中继续提升。本文不当之处多多指教哦。小编给大家推荐一篇写的不错的相关博客:http://www.cnblogs.com/wean/archive/2012/05/16/2502724.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 48
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值