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

时间: 2018-02-05

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

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

详解Hibernate缓存与性能优化

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

java中hibernate二级缓存详解

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

详细解读Hibernate的缓存机制

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

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

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

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

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

hibernate查询缓存详细分析

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

详解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

详解Spring boot使用Redis集群替换mybatis二级缓存

1 . pom.xml添加相关依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <!-- 依赖 --> <dependencies> &l

详解Spring Boot集成MyBatis(注解方式)

MyBatis是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集.spring Boot是能支持快速创建Spring应用的Java框架.本文通过一个例子来学习Spring Boot如何集成MyBatis,而且过程中不需要XML配置. 创建数据库 本文的例子使用MySQL数据库,首先创建一个用户表,执行sql语句如下: CREATE TABLE IF NOT EXISTS user ( `id` INT(10) NOT NULL A

详解spring Boot 集成 Thymeleaf模板引擎实例

今天学习了spring boot 集成Thymeleaf模板引擎.发现Thymeleaf功能确实很强大.记录于此,供自己以后使用. Thymeleaf: Thymeleaf是一个java类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层. Thymeleaf还提供了额外的模块与Spring MVC集成,所以我们可以使用Thymeleaf完全替代jsp. spring Boot 通过org.springframework.boot.autoconfigu

详解Spring Boot 集成Shiro和CAS

请大家在看本文之前,先了解如下知识点: 1.Shiro 是什么?怎么用? 2.Cas 是什么?怎么用? 3.最好有spring基础 首先看一下下面这张图: 第一个流程是单纯使用Shiro的流程. 第二个流程是单纯使用Cas的流程. 第三个图是Shiro集成Cas后的流程. PS:流程图急急忙忙画的,整体上应该没有什么问题,具体细节问题还请大家留言指正. 如果你只是打算用到你的Spring Boot项目中,那么看着如下配置完成便可. 如果你想进一步了解其中的细节,还是建议大家单独配置Shiro.单

详解spring boot集成RabbitMQ

RabbitMQ作为AMQP的代表性产品,在项目中大量使用.结合现在主流的spring boot,极大简化了开发过程中所涉及到的消息通信问题. 首先正确的安装RabbitMQ及运行正常. RabbitMQ需啊erlang环境,所以首先安装对应版本的erlang,可在RabbitMQ官网下载 # rpm -ivh erlang-19.0.4-1.el7.centos.x86_64.rpm 使用yum安装RabbitMQ,避免缺少依赖包引起的安装失败 # yum install rabbitmq-s

详解Spring MVC 集成EHCache缓存

废话少说,直接上代码: ehcache.xml 文件 <?xml version="1.0" encoding="UTF-8"?> <ehcache dynamicConfig="false" monitoring="off" updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

详解spring boot jpa整合QueryDSL来简化复杂操作

前言 使用过spring data jpa的同学,都很清楚,对于复杂的sql查询,处理起来还是比较复杂的,而本文中的QueryDSL就是用来简化JPA操作的. Querydsl定义了一种常用的静态类型语法,用于在持久域模型数据之上进行查询.JDO和JPA是Querydsl的主要集成技术.本文旨在介绍如何使用Querydsl与JPA组合使用.JPA的Querydsl是JPQL和Criteria查询的替代方法.QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查

详解spring boot rest例子

简介:本文将帮助您使用 Spring Boot 创建简单的 REST 服务. 你将学习 什么是 REST 服务? 如何使用 Spring Initializr 引导创建 Rest 服务应用程序? 如何创建获取 REST 服务以检索学生注册的课程? 如何为学生注册课程创建 Post REST 服务? 如何利用 postman 执行 rest 服务? 本教程使用的 rest 服务 在本教程中,我们将使用适当的 URI 和 HTTP 方法创建三个服务: @GetMapping("/ students

详解spring boot starter redis配置文件

spring-boot-starter-Redis主要是通过配置RedisConnectionFactory中的相关参数去实现连接redis service. RedisConnectionFactory是一个接口,有如下4个具体的实现类,我们通常使用的是JedisConnectionFactory. 在spring boot的配置文件中redis的基本配置如下: # Redis服务器地址 spring.redis.host=192.168.0.58 # Redis服务器连接端口 spring.