Hibernate探索之路(四)——缓冲

引言:

              缓存就是数据交换的缓冲区(称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。——这个缓冲的概念是我们传统的计算机缓冲概念,也称之为高速缓存区(Cache)。我们今天学习的内容是hibernate框架有关于缓冲的原理与应用。

概述:

              hibernate缓存介于应用程序和数据库之间,其作用是降低应用程序直接访问数据库,进行读写数据操作的频率,从而提高应用程序的性能。这里提到的应用程序可以看作是一个线程,也就是提高了线程的执行效率。

内容:

              一 原理

               hibernate缓冲的核心原理将数据库中的数据复制到该缓冲中,线程执行过程中,直接读写缓冲中的数据,只在某些特定的时刻按照缓冲中的数据来同步更新数据库。通俗理解,线程再访问数据库之前,先去缓冲中查看是否有想要的数据,如果有就取出缓冲中的数据,否则再去数据库中检索相应的数据,同时复制到缓冲中。

               缓冲的物理介质通常都是内存,而数据库的存储介质通常都是硬盘或者磁盘,所以访问缓冲的性能优于访问数据库。

              

              二 分类

             1 hibernate的缓存狭义上分为第一级缓存和第二级缓存。广义上可以分为session缓存和SessionFactory缓存。为什么这么说,因为SessionFactory缓存分为内置缓存和外置缓存,而我们常说的hibernate第二级缓存就是SessionFactory的外置缓存。

                 (1)第一级缓存(Session缓存):Session缓存是内置的,不能被拆卸,代表一块内存空间,在这个空间存放了相互关联的持久化java对象,Session负责根据持久化对象的状态变化来同步更新数据库。

                 (2)第二级缓存(SessionFactory外置缓冲):代表进程或集群范围内的缓存,该缓存中存放的是对象的散装数据。同时该缓存是可配置的插件,实现起来相对复杂,需要考虑并发访问策略和数据过期策略等规范。谈到并发访问策略,简单总结一下分为如下四种策略,每种策略对应一种事务隔离级别:

                          

                   接着介绍一些常用的第二级缓存插件:

                   

             2 缓存的存在具有特定的生命周期,缓存的生命周期与持久化层的缓存的范围息息相关,于是依据这个关系,可以将缓存分为事务级、进程级、集群级。

                 (1) 事务级:缓存只能被当前事务访问,缓存的生命周期依赖于事务的生命周期,事务结束,缓存也结束生命周期。这里的事务可以是数据库事务或者应用事务。每个事务都有独自的缓存,缓存内的数据通常采用相互关联的对象形式。

                 (2) 进程级:该缓存被进程的所有事务共享,缓存的生命周期同样依赖于进程的生命周期。由于进程范围的缓存可能会存放大量数据,物理介质可以是内存或硬盘,缓存内的数据可以采用相互关联的对象形式,也可以采用对象的散装数据形式(类似于序列化形式),性能更优于序列化形式。但是进程级别的缓存支持多个线程共享数据,可能会导致并发访问缓存的问题,有必要对缓存采取事务隔离机制。

                 (3) 集群级:在集群环境中,缓存被同一个机器或多个机器上的多个进程共享,缓存的数据被复制到集群环境中的每个进程节点,进程之间通过远程通信来保证缓存中数据的一致性,缓存中的数据采用对象的散装数据形式。

              三 配置和使用

             第一级缓冲:  当程序调用Session的save()、update()、saveOrUpdate()、load()或get()方法,以及调用Query查询接口的list()、iterate()或filter()方法时,缓存中不存在相应对象,hibernate会把该对象加入到第一级缓存中,当清理缓存时,同步数据库。

                     测试实例代码:

package com.bjpowernode.hibernate;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Session;

import junit.framework.TestCase;

public class CacheTest extends TestCase {

	/**
	 * 在同一个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());
			
			//不会发出查询语句,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);
		}
	}	
}

             2  第二级缓冲:  

                  1)将ehcache.xml文件拷贝到src下

     2)hibernate.cfg.xml文件的配置      

<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_cache</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">bjpowernode</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="hibernate.show_sql">true</property>
		
		<!-- 配置缓存提供商 -->
		<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-factory>
</hibernate-configuration>
     3)测试类中的代码: 
package com.bjpowernode.hibernate;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import org.hibernate.CacheMode;
import org.hibernate.Session;

import junit.framework.TestCase;

public class CacheTest extends TestCase {

	/**
	 * 开启二级缓存
	 * 
	 * 在两个session中发load查询
	 */
	public void testCache1() {
		//初始化session
		Session session = null;
		try {
			//得到一个session对象
			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 {
			//关闭session对象
			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);
		}
		
	}
}

             3  查询缓存

                1)在前面二级缓存配置文件hibernate.cfg.xml填入如下代码

<!-- 启用查询缓存,默认是false是不起用的 -->
		<property name="hibernate.cache.use_query_cache">true</property>
               2)测试代码: 
/**
	 * 开启查询,关闭二级缓存,采用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);
		}
	}
}
          

总结:

              hibernate实际分为第一级缓存和第二级缓存,查询缓存严格说不能是单独划分出来,因为它是在二级缓存的基础上扩展的。

        一级缓存:1  生命周期与session的生命周期一致,都很短,也叫做事务级缓存;2 缓存实体对象,普通对象不予处理;3 在同一个Session中先调用save,再调用load查询刚刚save的数据;4  load、get、iterate查询实体对象或大批量数据更新方法 ; 5 优点:减少数据库访问频率,提高数据访问性能;当缓存中的持久化对象之间存在循环关联关系时,session会保证不出现访问对象死循环以及由于死循环引起的JVM堆栈溢出。

        二级缓存:1 二级缓存生命周期与SessionFactory生命周期一致,SessionFactory可以管理二级缓存;2 使用原则:通常读远远大于写,不经常改变的实体对象;3 用处:用来缓存实体对象,大批量更新数据时,如果配置了二级缓存建议禁用一级缓存和二级缓存的交互。

        查询缓存:1  缓存对象:普通属性结果集,实体对象的结果集id ;2  生命周期:当关联的表发生修改时,查询缓存的生命周期结束。

        各位看客“老爷”,哪里有建议,请在评论中指出,我们共同交流进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值