【Hibernate】一级、二级缓存

    本文讲述HIbernate中一级、二级缓存的概念以及如何使用。

    一、大纲

    2.什么是一级缓存

    3.一级缓存示例展示

    4.二级缓存以及示例展示

    5.总结


    二、什么是一级缓存

    在hibernate中所谓的一级缓存就是session对象,但是一级缓存对提高性能的作用性并不是很大,其session主要的目的是管理实体对象的状态(临时、离线、持久化)。

    其缓存模型如下图:

    技术分享


    三、一级缓存示例

    3.1 查询(get,load, HQL)

    3.1.1 get/load方法

@Test
	public void testGet() {
		Session session = factory.openSession();
		Transaction tx = session.beginTransaction();
		tx.begin();
		// 第一次查询
		User user1 = (User)session.get(User.class, 1);
		System.out.println(user1);
		// 如果session中存在id=1的User对象缓存,那么再次查询时,将不需要执行SQL
		User user2 = (User)session.get(User.class, 1);
		System.out.println(user2);
		tx.commit();
		session.close();
	}

    技术分享

    如果执行第二次查询在session关闭之后,那么会抛出异常,session已经关闭。load方法类型,只是其支持懒加载查询。

    3.1.2 Hql查询

@Test
	public void testHqlQueryFromCache() throws Exception {
		Session session = factory.openSession();
		Transaction tx = session.beginTransaction();
		tx.begin();
		// 使用HQL查询id=1的User对象,此时会将id=1的对象写入到session缓存中
		User user1 = (User)session.createQuery("FROM User WHERE id = 1").uniqueResult();
		System.out.println(user1);
		System.out.println("================");
		// 执行第二次同样查询,默认情况无法从session中获取id=1的对象,但是也会将id=1的对象写入session缓存中
		User user2 = (User)session.createQuery("FROM User WHERE id = 1").uniqueResult();
		System.out.println(user2);
		System.out.println("================");
		// 使用get方法查询,由于session中已经存在id=1的对象,所以不会执行SQL
		User user3 = (User)session.get(User.class, 1);
		System.out.println(user3);
		tx.commit();
		session.close();
	}

    技术分享

    3.2 Update/delete对Session的影响

    在session执行查询之后,如果此时执行update或者delete操作,session中的数据是否会发生变化呢?通过下面这个例子展示:

@Test
	public void testUpdate() {
		Session session = factory.openSession();
		Transaction tx = session.beginTransaction();
		tx.begin();
		// 执行查询操作,将id=1的数据,存放在session中
		User user = (User)session.get(User.class, 1);
		System.out.println(user);
		// 执行update操作,将id=1的数据修改
		session.createQuery("UPDATE User SET name = ? WHERE id = 1").setParameter(0, "updateOrDelete").executeUpdate();
		// 如果执行的update操作,修改了session中的实体name属性,那么获取时将得到updateOrDelete值,否则保持变
		User user2 = (User)session.get(User.class, 1);
		System.out.println(user2);
		tx.commit();
		session.close();
	}

    技术分享    得出结论update或者delete(这里未展示delete操作),不会修改当前session的数据,只能等待下次session查询,或者手动使用session.refresh()/clear等刷新session数据。

    

    四、二级缓存以及示例展示

    4.1 什么是二级缓存

    二级缓存实际上是提供了一个公共数据缓存库,用于提高查询性能,当对数据的实时性要求非常高时,没有必要使用二级缓存,二级缓存模型如下:

    技术分享    可以理解为二级缓存是不同session的共享数据,而一级缓存是针对同一个session操作的。

    4.2 配置二级缓存

    Hibernate默认并不使用二级缓存,如果使用的话,需要配置一些参数提供使用;

    1、配置二级缓存类型(提供者)《hibernate.cfg.xml配置中》

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

    2、设置需要配置的实体类、集合、查询条件

<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- 实体类 -->
<class-cache usage="read-write" class="com.hibernate.seconde_cache.User" />
<!-- 注:也可以在实体映射文件中配置<cache>这里不做介绍 -->

    4.3 各种类型缓存处理

    在4.2中粗略的展示了一些基本的二级缓存配置,在此节中将主要展示三种情况二级缓存配置。

    4.3.1 单个实体二级缓存

    1)配置

<!-- 设置缓存提供者 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 实体类 -->
<class-cache usage="read-write" class="com.hibernate.seconde_cache.User" />

    2)示例展示

@Test
	public void testSingleEntity() throws Exception {
		// 第一个session,存储数据
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		User user1 = (User)session1.get(User.class, 1);
		System.out.println(user1);
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		// 第二个session读取第一个session的数据
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		User user2 = (User)session2.get(User.class, 1);
		System.out.println(user2);
		tx2.commit();
		session2.close();
	}

技术分享

    3)update和delete对二级缓存的影响

	@Test
	public void testSingleEntityOfUpdate() throws Exception {
		// 第一个session,存储数据
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		User user1 = (User)session1.get(User.class, 1);
		System.out.println(user1);
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		Session updateSession = factory.openSession();
		updateSession.createQuery("UPDATE User SET name=? WHERE id = 1").setParameter(0, "Second Cache").executeUpdate();
		
		System.out.println("\n===================\n");
		
		// 第二个session读取第一个session的数据
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		User user2 = (User)session2.get(User.class, 1);
		System.out.println(user2);
		tx2.commit();
		session2.close();
	}

技术分享

    由此可以update和delete的操作,会即使影响二级缓存的操作,其他session可以及时发现

    4.3.2 查询条件缓存

    在介绍如何缓存查询条件之前,如何通过Hibernate提供的方法实现缓存,并解释此种方法的不良效果,以及引入查询条件如何缓存。

    1)非查询条件实现缓存(缓存配置同4.3.1)

    使用list执行相同查询条件,依旧重复执行SQL

@Test
	public void testHql() throws Exception {
		// 第一个session,存储数据
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		List user1= session1.createQuery("FROM User").list();
		System.out.println(user1);
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		// 第二个session读取第一个session的数据
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		List user2 = session2.createQuery("FROM User").list();
		System.out.println(user2);
		tx2.commit();
		session2.close();
	}

   

技术分享

    使用iterate方法替代list方法,但是会出现N+1此查询问题,先查询符合条件的Id,之后在根据Id查询。

@Test
	public void testHqlOfIterate() throws Exception {
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		// 使用iterate方法替代List方法
		Iterator<User> users1= session1.createQuery("FROM User").iterate();
		while(users1.hasNext()) {
			System.out.println(users1.next());
		}
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		Iterator<User> users2= session2.createQuery("FROM User").iterate();
		while(users2.hasNext()) {
			System.out.println(users2.next());
		}
		tx2.commit();
		session2.close();
	}

技术分享

    2)使用查询条件缓存

    配置相比于4.3.1需要添加:

<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>

    就如1)中所属,使用查询条件过滤,可以减少1)中的查询次数,大大提高查询效率:

@Test
	public void testHqlOfCondition() throws Exception {
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		// 通过执行setCacheable(true)方法,表示将此次查询条件缓存
		// 也就是以FROM Use WHERE id < 4为键存储,值:对应查询的结果集
		List list1= session1.createQuery("FROM User WHERE id < 4").setCacheable(true).list();
		System.out.println(list1);
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		List list2= session2.createQuery("FROM User WHERE id < 4").setCacheable(true).list();
		System.out.println(list2);
		tx2.commit();
		session2.close();
	}

技术分享

    注:查询条件必须保持一直,否则视为不同查询条件。delete和update操作与4.3.1的影响一致,这里不做效果展示。

    4.3.3 集合元素查询缓存展示

    示例实体映射模型为(班级、学生方式《多对1的关系》),具体的映射代码、实体类代码这里不展示。

    1)配置(相比4.3.1添加如下配置)

<!-- 配置班级实体缓存 -->
<class-cache usage="read-write" class="com.hibernate.seconde_cache.Classes"/>
<!-- 配置学生实体缓存 -->
<class-cache usage="read-write" class="com.hibernate.seconde_cache.Student"/>
<!-- 配置班级中的学生集合缓存,注意使用的方式是类全限定名.属性名 -->
<collection-cache usage="read-write" collection="com.hibernate.seconde_cache.Classes.students"/>

    2)代码示例:

	@Test
	public void testSet() throws Exception {
		Session session1 = factory.openSession();
		Transaction tx1 = session1.beginTransaction();
		tx1.begin();
		// 查询id=1的班级
		Classes classes1 = (Classes)session1.get(Classes.class, 1);
		System.out.println(classes1);
		// 执行懒加载获取数据;如果想将集合元素也缓存,那么必须添加集合配置
		System.out.println(classes1.getStudents());
		
		tx1.commit();
		session1.close();
		
		System.out.println("\n===================\n");
		
		Session session2 = factory.openSession();
		Transaction tx2 = session2.beginTransaction();
		tx2.begin();
		// 第二个session,获取二级缓存中的数据
		Classes classes2 = (Classes)session2.get(Classes.class, 1);
		System.out.println(classes2);
		System.out.println(classes2.getStudents());
		tx2.commit();
		session2.close();
	}

技术分享

    如果没有添加

<collection-cache usage="read-write" collection="com.hibernate.seconde_cache.Classes.students"/>

    那么会出现如下结果:

技术分享

    五、总结

    涉及到的二级缓存知识,远远比本文多,一般不会使用本文中的Hashtable提供者,通常使用:

技术分享

    1级缓存是必须使用的,因为任何操作都需要使用到session,但是其对性能提高影响不大,而二级缓存对那些对数据实时性以及准确性要求不高的情况,建议使用,可以提高查询性能。

本文出自 “java程序冥” 博客,请务必保留此出处http://793404905.blog.51cto.com/6179428/1617287

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。