Hibernate缓存机制实例代码解析

本文研究的主要是Hibernate缓存机制的相关内容,具体如下。

演示项目:

Student.java:

public class Student {
	/*学生ID*/
	private int id;
	/*学生姓名*/
	private String name;
	/*学生和班级的关系*/
	private Classes classes;
	//省略setter和getter方法
}

Classes.java:

public class Classes {
	/*班级ID*/
	private int id;
	/*班级名称*/
	private String name;
	/*班级和学生的关系*/
	private Set<Student> students;
	//省略setter和getter方法
}

Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
  <class name="Student" table="t_student">
    <id name="id">
      <generator class="native"/>
    </id>
    <!-- 映射普通属性 -->
    <property name="name"/>
    <!-- 多对一 映射,在多的一端加上外键-->
    <many-to-one name="classes" column="classesid"/>
  </class>
</hibernate-mapping> 

Classes.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
  <!-- 设置lazy为false -->
  <class name="Classes" table="t_classes" lazy="false">
    <id name="id">
      <generator class="native"/>
    </id>
    <property name="name"/>
    <!-- 一对多映射 ,inverse="true"表示交给对端维护关系-->
    <set name="students" inverse="true">
       <key column="classesid"/>
      <one-to-many class="Student"/>
    </set>
  </class>
</hibernate-mapping> 

一级缓存:

一级缓存声明周期很短和session的生命周期一致,一级缓存也叫session级的缓存或事物级缓存,一级缓存是缓存对象的,并不能缓存属性。

测试方法(在同一个session中使用load()查询两次):

/*取出来之后会放在缓存中,第二次取的时候会直接从缓存取出*/
      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即缓存即一级缓存中的。第二次load()后及时去获取值的时候也没有在发出语句到数据库中查询了,而是直接从缓存中取值了(必须是在同一session中)。

测试方法二(在同一session中):

Student student = new Student();
      student.setName("张三");
      Serializable id = session.save(student);
      student = (Student)session.load(Student.class, id);
      //不会发出查询语句,因为save支持缓存
      System.out.println("student.name=" + student.getName()); 

注:调用了save()方法再使用load()去加载对象,然后真正获取name属性,但是此时并不会发出语句去查询数据库。因为save()方法也是支持缓存的。

测试大批量数据的添加:

public void testCache7() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      for (int i=0; i<100; i++) {
        Student student = new Student();
        student.setName("张三" + i);
        session.save(student);
        //每20条更新一次
        if (i % 20 == 0) {
          //清除缓存,调用flush之后数据就会保存到数据库
          session.flush();
          //清除缓存的内容
          session.clear();
        }
      }
      session.getTransaction().commit();
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    }finally {
      HibernateUtils.closeSession(session);
    }
  } 

注:

1.因为save()方法支持缓存,那就存在一个问题,如果我要同时存1000万条数据,那缓存中岂不是有1000万的缓存对象,那就很可能导致溢出。所以说Hibernate并不能很好的支持大批量数据的更新操作,但是我们也可以很灵活的处理这个问题,比如使用循环每20条数据清除一次缓存。

2.save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。

二级缓存:

二级缓存也称为进程级缓存或SessionFactory级缓存,二级缓存可以被所有的session缓存共享。二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存,二级缓存的原则是当读远大于写的时候使用,二级缓存也主要是缓存实体对象的。

二级缓存的配置:

1.将ehcahe.xml文件拷贝到src目录下。

2.在Hibernate.cfg.xml文件中加入缓存产品提供商,如下:

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> 

3.启用二级缓存(可以不显示启动,因为默认就是启用的),如下:

<property name="hibernate.cache.use_second_level_cache">true</property>

4.指定哪些实体类使用二级缓存。

5.导入缓存使用的接口jar包:lib\optional\ehcache\ehcache-core-2.4.3.jar

ehcache.xml文件的内容:

<defaultCache
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    /> 

注:

1.maxElementsInMemory表示缓存中最多存放的对象。

2.eternal表示是否永远不过期(设置为false更有实际意义,如果为true的话表示永远不过期,那么下面的属性都没什么意义了)。

3.timeToIdleSecods表示一个对象第一次被访问后经过多长时间没被访问就清除。

4.timeToLiveSecods表示一个对象的存货时间。

5.overflowToDisk为true表示缓存中超出了maxElementsInMemory指定的个数就存到磁盘中。

指定溢出时保存的磁盘路径:

<diskStore path="java.io.tmpdir"/>

注:这个路径可以改。

测试方法(一级缓存的前提是必须在同一个session,现在我们使用二级缓存来看看在两个不同的session中是否存在缓存):

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);
	}
}

注:如果配置了二级缓存,我们会发现,即使第一个session关闭了,再开启另外一个session去加载数据也不会发出语句到数据库中去查询数据,因为配置了二级缓存,它是整个sessionFactory共享的。

禁用二级缓存实现大批量数据的添加:

public void testCache5() {
    Session session = null;
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction(); 

      //禁止一级缓存和二级缓存交互
      session.setCacheMode(CacheMode.IGNORE);
      for (int i=0; i<100; i++) {
        Student student = new Student();
        student.setName("张三" + i);
        session.save(student);
        //每20条更新一次
        if (i % 20 == 0) {
          session.flush();
          //清除缓存的内容
          session.clear();
        }
      }
      session.getTransaction().commit();
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    }finally {
      HibernateUtils.closeSession(session);
    }
  }  

注:session.flush()表示清除一级缓存,但是我们又开起了二级缓存,save()之后也保存到了二级缓存,还是存在缓存过大导致溢出的情况。所以这种情况下我们应该禁用二级缓存:session.setCacheMode(CacheMode.IGNORE);

查询缓存:一级缓存和二级缓存都是缓存实体对象的,但是有些时候我们希望获取某些属性的时候也不要频繁的去访问数据库,而是从缓存中获取,此时我们就可以使用查询缓存。另外查询缓存对实体对象的结果集会缓存ID。查询缓存的生命周期,当关联的表发生修改,查询缓存的声明周期就结束,和session的生命周期无关。

配置查询缓存:

1.修改hibernate.cfg.xml文件,来开启查询缓存,默认是false即不开启的,应如下设置:

<property name="hibernate.cache.use_query_cache">true</property> 

2.必须在程序中启用,如:

query.setCacheable(true) 

测试方法:

public void testCache2() {
	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);
		}
		session.getTransaction().commit();
	}
	catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}
	finally {
		HibernateUtils.closeSession(session);
	}
	System.out.println("-------------------------------------------------------");
	try {
		session = HibernateUtils.getSession();
		session.beginTransaction();
		//不会发出查询语句,因为查询缓存和session的生命周期没有关系
		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);
		}
		session.getTransaction().commit();
	}
	catch(Exception e) {
		e.printStackTrace();
		session.getTransaction().rollback();
	}
	finally {
		HibernateUtils.closeSession(session);
	}
}

注:上述代码中,我们关闭了二级缓存,开启了查询缓存,然后查询普通属性。运行测试代码我们可以发现,在第一个session中第一次查询发出了一条语句,然后关闭了session,接着再第二个session中进行查询,我们会发现第二个session中的查询并没有发出语句,这说明查询缓存和session的生命周期没有什么关系。

hibernate.cfg.xml的缓存配置:

<!-- 设置指定二级缓存的实现接口 -->
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>
    <!-- 设置二级缓存所使用的配置文件 -->
    <property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
    <!-- 设置使用QUERY查询缓存 -->
    <property name="hibernate.cache.use_query_cache">true</property> 

    <!-- 加载对象关系映射文件 -->
    <mapping resource="com/lixue/bean/Classes.hbm.xml" />
    <mapping resource="com/lixue/bean/Student.hbm.xml" /> 

    <!-- 必须先引入资源映射文件(就是实体映射文件)后再设置有使用二级缓存的实体类 -->
    <class-cache usage="read-only" class="com.lixue.bean.Student" /> 

总结

以上就是本文关于Hibernate缓存机制实例代码解析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • JSP 开发之hibernate配置二级缓存的方法

    JSP 开发之hibernate配置二级缓存的方法 hibernate二级缓存也称为进程级的缓存或SessionFactory级的缓存. 二级缓存是全局缓存,它可以被所有的session共享. 二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存. 常用的缓存插件 Hibernater二级缓存是一个插件,下面是几种常用的缓存插件: EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了

  • 详解spring boot集成ehcache 2.x 用于hibernate二级缓存

    本文将介绍如何在spring boot中集成ehcache作为hibernate的二级缓存.各个框架版本如下 spring boot:1.4.3.RELEASE spring framework: 4.3.5.RELEASE hibernate:5.0.1.Final(spring-boot-starter-data-jpa默认依赖) ehcache:2.10.3 项目依赖 <dependency> <groupId>org.springframework.boot</gro

  • 详解Hibernate缓存与性能优化

    缓存概念 缓存 介于应用程序和永久性数据源(文件,数据库等)之间,作用就是降低应用程序直接读取数据源的频率,从而提高应用程序的运行性能.缓存中的数据就是数据源中数据的复制,应用程序在运行时直接读取缓存中的数据. 缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘或磁盘,应用程序读写内存的速度显然比读写硬盘的速度快.如果缓存存放的数据非常大,也会用硬盘作为缓存的物理介质. Hibernate缓存分类 在hibernate中提供了二种缓存机制:一级缓存.二级缓存,因为二级缓存策略是针对

  • java中hibernate二级缓存详解

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

  • 详细解读Hibernate的缓存机制

    一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为"Session的

  • hibernate查询缓存详细分析

     一.查询缓存配置 1.在hibernate.cfg.xml中加入查询缓存的策略,  <propertyname="hibernate.cache.use_query_cache">true</property>      启用查询缓存的策略,默认是false. 二.关闭二级缓存,采用query.list()查询普通属性 代码如下所示. public voidtestCache1() { Session session = null; try { session

  • Spring 整合 Hibernate 时启用二级缓存实例详解

    Spring 整合 Hibernate 时启用二级缓存实例详解 写在前面: 1. 本例使用 Hibernate3 + Spring3: 2. 本例的查询使用了 HibernateTemplate: 1. 导入 ehcache-x.x.x.jar 包: 2. 在 applicationContext.xml 文件中找到 sessionFactory 相应的配置信息并在设置 hibernateProperties 中添加如下代码: <!-- 配置使用查询缓存 --> <prop key=&q

  • 详解Hibernate注解方式的二级缓存

    详解Hibernate注解方式的二级缓存 hibernate默认情况下是支持一级缓存,也就是session级的缓存的,而默认情况下是不支持二级缓存,即sessionFactory级的缓存的,二级缓存        一般比较少去考虑它,除非对效率要求非常高的时候, 这时侯如果我们的某一个实体要在多个session里面使用需要用到session间的缓存的时候就可以进行配置来实现二级缓存了! 在看文档的时候说可以在persistence.xml里面进行配置,但我一般是不用这个文件的,就直接使用注解!

  • Hibernate缓存机制实例代码解析

    本文研究的主要是Hibernate缓存机制的相关内容,具体如下. 演示项目: Student.java: public class Student { /*学生ID*/ private int id; /*学生姓名*/ private String name; /*学生和班级的关系*/ private Classes classes; //省略setter和getter方法 } Classes.java: public class Classes { /*班级ID*/ private int i

  • Spring缓存机制实例代码

    Spring的缓存机制非常灵活,可以对容器中任意Bean或者Bean的方法进行缓存,因此这种缓存机制可以在JavaEE应用的任何层次上进行缓存. Spring缓存底层也是需要借助其他缓存工具来实现,例如EhCache(Hibernate缓存工具),上层则以统一API编程. 要使用Spring缓存,需要以下三步 1.向Spring配置文件导入context:命名空间 2.在Spring配置文件启用缓存,具体是添加 <cache:annotation-driven cache-manager="

  • Spring的事务机制实例代码

    本文研究的主要是Spring的事务机制的相关内容,具体如下. JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持.而局部事务则与底层采用的持久化技术有关,如果底层直接使用JDBC,需要用Connection对象来操事务.如果采用Hibernate持久化技术,则需要使用session对象来操作事务. 通常的,使用JTA事务,JDBC事务及Hibern

  • JavaScript实现跑马灯抽奖活动实例代码解析与优化(一)

    最近做了个项目,其中有项目需求是要实现跑马灯抽奖效果,实现此功能主要用到js相关知识,废话不多说,感兴趣的朋友可以阅读下全文. 开始之前先来看上篇文章遗漏的两个问题和几个知识点,是自己重构的过程中需要用到的: 1.移动端1px像素线的问题 对于设计师给我的手机端网页的设计稿都是2倍图.按照道理来说,在写网页的时候,所有对象的实际尺寸都是会除2.但是对于1像素的线呢? 先来看两张图,设计稿的效果: 在三星 S4下的实际显示效果: 可以看到这个时候1px的线竟然显示不出来了.这个问题是跟 S4手机的

  • shiro缓存机实例代码

    Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现. Shiro提供的Cache接口: Java代码 public interface Cache<K, V> { //根据Key获取缓存中的值 public V get(K key) throws CacheException; //往缓存中放入key-value,返回缓存中之前的值 public V put(K key, V value) thr

  • MyBatis 动态SQL和缓存机制实例详解

    有的时候需要根据要查询的参数动态的拼接SQL语句 常用标签: - if:字符判断 - choose[when...otherwise]:分支选择 - trim[where,set]:字符串截取,其中where标签封装查询条件,set标签封装修改条件 - foreach: if案例 1)在EmployeeMapper接口文件添加一个方法 public Student getStudent(Student student); 2)如果要写下列的SQL语句,只要是不为空,就作为查询条件,如下所示,这样

  • Android获取应用程序大小和缓存的实例代码

    info package com.qin.appsize; import android.content.Intent; import android.graphics.drawable.Drawable; //Model类 ,用来存储应用程序信息 public class AppInfo { private String appLabel; //应用程序标签 private Drawable appIcon ; //应用程序图像 private Intent intent ; //启动应用程序

  • Java Classloader机制用法代码解析

    做Java开发,对于ClassLoader的机制是必须要熟悉的基础知识,本文针对Java ClassLoader的机制做一个简要的总结.因为不同的JVM的实现不同,本文所描述的内容均只限于Hotspot Jvm. 本文将会从JDK默认的提供的ClassLoader,双亲委托模型,如何自定义ClassLoader以及Java中打破双亲委托机制的场景四个方面入手去讨论和总结一下. JDK默认ClassLoader JDK 默认提供了如下几种ClassLoader Bootstrp loader Bo

  • 使用vue重构资讯页面的实例代码解析

    从我接手到将这个页面代码重构前,一直都还是使用angular1的代码去做的,需求来了也是用angular去实现:作为一个憧憬新技术的前端,怎么忍受得了现在还在使用这么有历史感的框架,所以,以前就一直在酝酿着如何将angular重构成vue. 代码结构设计 这个资讯项目代码整体都是使用angular.js来去实现的,而此次想重构的资讯详情页面只是其中的一个页面,所以新建了一个文件夹 /newApp .作为以后新技术的文件夹,以后使用vue技术的都放在这个文件夹下,区别于原先文件夹 /app . 在

  • AngularJs表单验证实例代码解析

    常用的表单验证指令如下详情: 1. 必填项验证 某个表单输入是否已填写,只要在输入字段元素上添加HTML5标记required即可: <input type="text" required /> 2. 最小长度 验证表单输入的文本长度是否大于某个最小值,在输入字段上使用指令ng-minleng= "{number}": <input type="text" ng-minlength="5" /> 3.

随机推荐