脚本之家 服务器常用软件
微信 投稿 交流社区 在线工具

Hibernate缓存机制实例代码解析

转载  发布时间:2018年02月08日 08:37:08   作者:lavimer   我要评论

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

  • hibernate
  • 缓存

相关文章

  • SSH框架网上商城项目第25战之使用java email给用户发送邮件

    这篇文章主要为大家详细介绍了SSH框架网上商城项目第25战之使用java email给用户发送邮件,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • mybatis基本实例详解

    这篇文章主要介绍了mybatis基本实例详解以及mybatis自由模糊查询,代码简单易懂,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-03-03
  • 详解Java的Spring框架中的注解的用法

    这篇文章主要介绍了Java的Spring框架中的注解的用法,包括对Java bean的定义的作用介绍,需要的朋友可以参考下
    2015-11-11
  • Java实现计算一个月有多少天和多少周

    这篇文章主要介绍了Java实现计算一个月有多少天和多少周,本文直接给出实例代码,需要的朋友可以参考下
    2015-06-06
  • Java语言描述MD5加密工具类实例代码

    这篇文章主要介绍了Java语言描述MD5加密工具类实例代码,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • Kotlin基础教程之数据类型

    这篇文章主要介绍了Kotlin基础教程之数据类型的相关资料,需要的朋友可以参考下
    2017-05-05
  • MyEclipse 2016 CI 4新增BootStrap模板

    MyEclipse2016是一款全球使用最为广泛的企业级开发环境程序,这篇文章主要介绍了MyEclipse 2016 CI 4新增BootStrap模板的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • java制作简单的坦克大战

    坦克大战是我们小时候玩红白机时代的经典游戏,看到有不少小伙伴都使用各种语言实现了一下,手痒痒,也使用java做的一个比较简单的坦克大战,主要面向于学过Java的人群,与学了一段时间的人,有利于面向对象思想的提高,推荐给大家。
    2015-03-03
  • Java8中字符串处理库strman-java的使用示例

    除了Java本身的字符串处理方式外,我们还可以使用Apache Common Langs里的StringUtils来简化String的操作。但以上两种方式对于我们日常编程中最容易碰到的字符串处理来说,仍然显得有些不足。所以这篇文章给大家介绍Java8中字符串处理库strman-java的使用。
    2016-09-09
  • 详解Java设计模式编程中的策略模式

    这篇文章主要介绍了详解Java设计模式编程中的策略模式,策略模式强调对对象的封装使用,比如文中举的锦囊妙计的例子便很生动,需要的朋友可以参考下
    2016-02-02

最新评论