资讯专栏INFORMATION COLUMN

Hibernate【缓存】知识要点

AlphaWallet / 1363人阅读

摘要:对象状态中对象的状态临时瞬时状态持久化状态游离状态学习的对象状态是为了更清晰地知道的设计思想,以及是一级缓存的基础当然啦,也就一点点知识临时瞬时状态当我们直接出来的对象就是临时瞬时状态的该对象还没有被持久化没有保存在数据库中不受的管理持久化

对象状态

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属性来设置

关闭懒加载:

    

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文件中开启二级缓存

        
        true
指定缓存框架

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

        
        org.hibernate.cache.HashtableCacheProvider
指定哪些类加入二级缓存
        
        
        

测试:

我们知道一级缓存是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中配置对象中的集合为二级缓存

        
        

测试代码:

    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      【开启查询缓存】

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

        
        true

在使用程序查询的时候,也要调用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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68715.html

相关文章

  • 纳税服务系统【总结】

    摘要:要是使用到日历的话,我们想到使用这个日历类上面仅仅是我个人总结的要点,如果有错误的地方还请大家给我指正。 纳税服务系统总结 纳税服务系统是我第一个做得比较大的项目(不同于javaWeb小项目),该项目系统来源于传智Java32期,十天的视频课程(想要视频的同学关注我的公众号就可以直接获取了) 我跟着练习一步一步完成需求,才发觉原来Java是这样用来做网站的,Java有那么多的类库,页面...

    ispring 评论0 收藏0
  • Java3y文章目录导航

    摘要:前言由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 前言 由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 由于更新比较频繁,因此隔一段时间才会更新目录导航哦~想要获取最新原创的技术文章欢迎关注我的公众号:Java3y Java3y文章目录导航 Java基础 泛型就这么简单 注解就这么简单 Druid数据库连接池...

    KevinYan 评论0 收藏0
  • Hibernate【inverse和cascade属性】知识要点

    摘要:属性属性表示控制权是否转移控制权已转移当前一方没有控制权控制权没有转移当前一方有控制权属性,是在维护关联关系的时候起作用的。表的外键并没有数据结论如果设置控制反转即然后通过部门方维护关联关系。 Inverse属性 Inverse属性:表示控制权是否转移.. true:控制权已转移【当前一方没有控制权】 false:控制权没有转移【当前一方有控制权】 Inverse属性,是在维护关联...

    abson 评论0 收藏0
  • Mybatis【逆向工程,缓存,代理】知识要点

    摘要:一级缓存值得注意的地方默认就是支持一级缓存的,并不需要我们配置和整合后进行代理开发,不支持一级缓存,和整合,按照的模板去生成代理对象,模板中在最后统一关闭。总结的一级缓存是级别的。 前言 本文主要讲解Mybatis的以下知识点: Mybatis缓存 一级缓存 二级缓存 与Ehcache整合 Mapper代理 使用Mapper代理就不用写实现类了 逆向工程 自动生成代码 ...

    wanglu1209 评论0 收藏0
  • Hibernate【映射】知识要点

    摘要:前言前面的我们使用的是一个表的操作,但我们实际的开发中不可能只使用一个表的因此,本博文主要讲解关联映射集合映射需求分析当用户购买商品,用户可能有多个地址。数据库表我们一般如下图一样设计数据库表,一般我们不会在表设计多个列来保存地址的。 前言 前面的我们使用的是一个表的操作,但我们实际的开发中不可能只使用一个表的...因此,本博文主要讲解关联映射 集合映射 需求分析:当用户购买商品,用户...

    TesterHome 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<