• Hibernate【缓存】知识要点


    对象状态

    Hibernate中对象的状态:

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

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

    临时/瞬时状态

    当我们直接new出来的对象就是临时/瞬时状态的..

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

    这里写图片描述

    持久化状态

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

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

    这里写图片描述

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

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

    这里写图片描述

    游离状态

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

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

    这里写图片描述


    一级缓存

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

    什么是一级缓存?

    Hibenate中一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数! 只在session范围有效! Session关闭,一级缓存失效!

    只要是持久化对象状态的,都受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种:

    • **<class-cache usage="read-only"/> 放入二级缓存的对象,只读; **
    • <class-cache usage="nonstrict-read-write"/> 非严格的读写
    • <class-cache usage="read-write"/> 读写; 放入二级缓存的对象可以读、写;
    • <class-cache usage="transactional"/> (基于事务的策略)

    集合缓存

    如果我们在数据库查询的数据是集合...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

  • 相关阅读:
    (转)Objective-C语法之动态类型(isKindOfClass, isMemberOfClass,id)等
    (转)Objective-C语言--属性和实例变量
    (转)iOS 属性字符串
    (转)git clone: error: RPC failed; result=18, HTTP code = 200 解决办法
    iOS设置UINavigationBar 的样式
    Install kubernetes without yum
    kubernetes install for centos
    Deploying docker registry v2
    build docker deivcemapper
    docker local registry server gave HTTP response to HTTPS client
  • 原文地址:https://www.cnblogs.com/Java3y/p/8531284.html
Copyright © 2020-2023  润新知