Hibernate【缓存】知识要点

对象状态

Hibernate中对象的状态:java

  • 临时/瞬时状态
  • 持久化状态
  • 游离状态

学习Hibernate的对象状态是为了更清晰地知道Hibernate的设计思想,以及是一级缓存的基础...固然啦,也就一点点知识sql

临时/瞬时状态

当咱们直接new出来的对象就是临时/瞬时状态的..数据库

  • 该对象尚未被持久化【没有保存在数据库中】
  • 不受Session的管理

这里写图片描述

持久化状态

当保存在数据库中的对象就是持久化状态了缓存

  • 当调用session的save/saveOrUpdate/get/load/list等方法的时候,对象就是持久化状态
  • 在数据库有对应的数据
  • 受Session的管理
  • 当对对象属性进行更改的时候,会反映到数据库中!

这里写图片描述

咱们来测试一下:当对对象属性进行更改的时候,会反映到数据库中!微信

session.save(idCard);
        idCard.setIdCardName("我是测试持久化对象");

复制代码

这里写图片描述

游离状态

当Session关闭了之后,持久化的对象就变成了游离状态了...session

  • 不处于session的管理
  • 数据库中有对应的记录

这里写图片描述


一级缓存

Hibernate有一级缓存和二级缓存之分,这里主要讲解一级缓存框架

什么是一级缓存?

Hibenate中一级缓存,也叫作session的缓存,它能够在session范围内减小数据库的访问次数! 只在session范围有效! Session关闭,一级缓存失效!ide

只要是持久化对象状态的,都受Session管理,也就是说,都会在Session缓存中!性能

Session的缓存由hibernate维护,用户不能操做缓存内容; 若是想操做缓存内容,必须经过hibernate提供的evit/clear方法操做学习

为何要是使用缓存?

减小对数据库的访问次数!从而提高hibernate的执行效率!

测试

咱们来看一下Hibernate是怎么减小对数据库访问的次数的。

如今个人User表有这么一条记录:

这里写图片描述

//把数据放进cache
        User user = (User) session.get(User.class, 1);

		//发现要修改的字段和cache同样,不执行
        user.setUserName("你好2");
复制代码

这里写图片描述

这里写图片描述

取数据也是同样的

User user = null;
        user = (User) session.get(User.class, 1);
        user = (User) session.get(User.class, 1);

复制代码

这里写图片描述


缓存相关的方法

和缓存有关经常使用的方法有三个:

  • session.flush(); 让一级缓存与数据库同步

  • session.evict(arg0); 清空一级缓存中指定的对象

  • session.clear(); 清空一级缓存中缓存的全部对象

  • clear

User user = null;
        user = (User) session.get(User.class, 1);

        //清除缓存,那么下面获取的时候,就不能从缓存里面拿了
        session.clear();
        user = (User) session.get(User.class, 1);

复制代码
  • flush

在有缓存的状况下,修改同一条记录的数据,以最后的为准...所以只有一条update

User user = null;
        user = (User) session.get(User.class, 1);

        user.setUserName("我是第一");
        user = (User) session.get(User.class, 1);
        user.setUserName("我是第二");

复制代码

这里写图片描述

我让强制让它和数据库同步的话,就有两条update了

User user = null;
        user = (User) session.get(User.class, 1);

        user.setUserName("我是第一");
 		session.flush();
        user = (User) session.get(User.class, 1);
        user.setUserName("我是第二");

复制代码

这里写图片描述

通常地,咱们在批处理的时候会用,由于缓存也是有大小的,若是1000条数据插入进去都要缓存,那么Hibernate可能就崩了...

  • 每隔必定记录数,先与数据库同步 flush()
  • 再清空缓存 clear()

值得注意的是:不一样的Session是不会共享缓存的!

Iterator与list

咱们使用HQL查询所有数据的时候,可使用list()获得全部的数据,也可使用iterator()获得一个迭代器,再遍历迭代器...那它们有什么区别呢?

。。。。当时看视频的时候说是下图:

这里写图片描述

可是我在测试的时候:List也能够获取缓存的数据

这里写图片描述

固然啦,Iterator也是能够获取缓存的数据

这里写图片描述

所以,在获取数据的时候仍是使用list()方便!

懒加载

懒加载就是当使用数据的时候才去获取数据、执行对应的SQL语句...当还没用到数据的时候,就不加载对应的数据!

主要目的就是为了提升Hibernate的性能,提升执行效率

  • get: 及时加载,只要调用get方法马上向数据库查询
  • load:默认使用懒加载,当用到数据的时候才向数据库查询。

懒加载再次体验

User user = (User) session.load(User.class, 1);

        System.out.println("________");
        System.out.println(user);

复制代码

这里写图片描述

咱们能够在对应的配置文件用一般lazy属性来设置

关闭懒加载:

<class name="IdCard" table="IdCard" lazy="false">
复制代码

这里写图片描述

lazy有三个属性:

  • true 使用懒加载
  • false 关闭懒加载
  • extra (在集合数据懒加载时候提高效率)【只有在set、list等集合标签中使用】
    • 在真正使用数据的时候才向数据库发送查询的sql;
    • 若是调用集合的size()/isEmpty()方法,只是统计,不真正查询数据!

懒加载异常

当Session关闭后,就不能使用懒加载了,不然会报出异常

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
复制代码

这里写图片描述

报出了这个异常,咱们有4种方法解决:

  • 方式1: 先使用一下数据
    • dept.getDeptName();
  • 方式2:强迫代理对象初始化
    • Hibernate.initialize(dept);
  • 方式3:关闭懒加载
    • 设置lazy=false;
  • **方式4: 在使用数据以后,再关闭session! **

Hibernate二级缓存

前面咱们已经讲解过了一级缓存,一级缓存也就是Session缓存,只在Session的范围内有效...做用时间就在Session的做用域中,范围比较小

Hibernate为咱们提供了二级缓存功能:二级缓存是基于应用程序的缓存,全部的Session均可以使用

  • Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!若是用户想用二级缓存,只须要在hibernate.cfg.xml中配置便可; 不想用,直接移除,不影响代码。
  • 若是用户以为hibernate提供的框架框架很差用,本身能够换其余的缓存框架或本身实现缓存框架均可以

这里写图片描述

Hibernate二级缓存:存储的是经常使用的类


配置二级缓存

既然二级缓存是Hibernate自带的,那么咱们能够在hibernate.properties文件中找到对应的信息..

这里写图片描述

  • #hibernate.cache.use_second_level_cache false【二级缓存默认不开启,须要手动开启】
  • #hibernate.cache.use_query_cache true 【开启查询缓存】
  • choose a cache implementation 【二级缓存框架的实现】

  • #hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
  • #hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
  • hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默认实现
  • #hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
  • #hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
  • #hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider

经过配置文件咱们能够发现,二级缓存默认是不开启的,须要咱们手动开启,如下步骤:

  • 1)开启二级缓存
  • 2)指定缓存框架
  • 3)指定哪些类加入二级缓存

开启二级缓存

在hibernate.cfg.xml文件中开启二级缓存

<!-- a. 开启二级缓存 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
复制代码

指定缓存框架

指定Hibernate自带的二级缓存框架就行了

<!-- b. 指定使用哪个缓存框架(默认提供的) -->
		<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
复制代码

指定哪些类加入二级缓存

<!-- c. 指定哪一些类,须要加入二级缓存 -->
        <class-cache usage="read-write" class="zhongfucheng.aa.Monkey"/>
        <class-cache usage="read-only" class="zhongfucheng.aa.Cat"/>
复制代码

测试:

咱们知道一级缓存是Session的缓存,那么咱们在测试二级缓存的时候使用两个Session来测试就行了。若是第二个Session拿到的是缓存数据,那么就证实二级缓存是有用的。

package zhongfucheng.aa;

import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

public class App5 {
    public static void main(String[] args) {


        //获取加载配置管理类
        Configuration configuration = new Configuration();
        //加载类对应的映射文件!
        configuration.configure().addClass(Animal.class);
        //建立Session工厂对象
        SessionFactory factory = configuration.buildSessionFactory();
        //获得Session对象
        Session session1 = factory.openSession();
        //使用Hibernate操做数据库,都要开启事务,获得事务对象
        Transaction transaction = session1.getTransaction();
        //开启事务
        transaction.begin();
        Monkey monkey = (Monkey) session1.get(Monkey.class,"40283f815be67f42015be67f43240001" );
        System.out.println(monkey.getName());
        System.out.println("-----------------------");



        Session session2 = factory.openSession();
        Transaction transaction2 = session2.getTransaction();
        transaction2.begin();
        Monkey monkey2 = (Monkey) session1.get(Monkey.class, "40283f815be67f42015be67f43240001");
        System.out.println(monkey2.getName());


        //提交事务
        transaction.commit();
        transaction2.commit();

        //关闭Session
        session1.close();
        session2.close();


    }
}

复制代码

获得的是缓存数据!

这里写图片描述


缓存策略

咱们在把Animal类放进二级缓存的时候,用法为只读

这里写图片描述

也就是说,只能读取,不能写入,咱们来看看写入会怎么样:

monkey2.setName("小猴子");
复制代码

抛出了异常....

这里写图片描述


usage的属性有4种:

  • ** 放入二级缓存的对象,只读; **
  • 非严格的读写
  • 读写; 放入二级缓存的对象能够读、写;
  • (基于事务的策略)

集合缓存

若是咱们在数据库查询的数据是集合...Hibernate默认是没有为集合数据设置二级缓存的...所以仍是须要去读写数据库的信息

接下来,咱们就看看把集合设置为二级缓存是什么作的:

  • 在hibernate.cgf.xml中配置对象中的集合为二级缓存
<!-- 集合缓存[集合缓存的元素对象,也加加入二级缓存] -->
		<collection-cache usage="read-write" collection="cn.itcast.b_second_cache.Dept.emps"/>
复制代码
  • 测试代码:
public void testCache() {
		Session session1 = sf.openSession();
		session1.beginTransaction();
		// a. 查询一次
		Dept dept = (Dept) session1.get(Dept.class, 10);
		dept.getEmps().size();// 集合
		session1.getTransaction().commit();
		session1.close();
		
		System.out.println("------");
		
		// 第二个session
		Session session2 = sf.openSession();
		session2.beginTransaction();
		// a. 查询一次
		dept = (Dept) session2.get(Dept.class, 10);  // 二级缓存配置好; 这里不查询数据库
		dept.getEmps().size();
		
		session2.getTransaction().commit();
		session2.close();
	}
复制代码

查询缓存

list()和iterator()会把数据放在一级缓存,但一级缓存只在Session的做用域中有效...若是想要跨Session来使用,就要设置查询缓存

咱们在配置文件中还看到了查询缓存这么一条配置..

#hibernate.cache.use_query_cache true      【开启查询缓存】
复制代码

也就是说,默认的查询数据是不放在二级缓存中的,若是咱们想要把查询出来的数据放到二级缓存,就须要在配置文件中开启

<!-- 开启查询缓存 -->
		<property name="hibernate.cache.use_query_cache">true</property>
复制代码
  • 在使用程序查询的时候,也要调用setCacheable()方法,设置为查询缓存。
@Test
	public void listCache() {
		Session session1 = sf.openSession();
		session1.beginTransaction();
		// HQL查询 【setCacheable 指定从二级缓存找,或者是放入二级缓存】
		Query q = session1.createQuery("from Dept").setCacheable(true);
		System.out.println(q.list());
		session1.getTransaction().commit();
		session1.close();
		
		
		Session session2 = sf.openSession();
		session2.beginTransaction();
		q = session2.createQuery("from Dept").setCacheable(true);
		System.out.println(q.list());  // 不查询数据库: 须要开启查询缓存
		session2.getTransaction().commit();
		session2.close();
	}
复制代码

若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够关注微信公众号:Java3y

相关文章
相关标签/搜索