Hibernate缓存以及哪些操作会向缓存中读取和存放数据

Hibernate缓存以及哪些操作会向缓存中读取和存放数据


Hibernate缓存

Hibernate有两级缓存,分别是一级缓存和二级缓存。一级缓存也叫Session级缓存,默认情况下就可以用,无需配置。一级缓存生命周期由Session对象决定,Session对象关闭,一级缓存也就消失。二级缓存也叫SessionFactory级缓存,需要配置后才能使用。二级缓存的生命周期比一级缓存的生命周期长,由SessionFactory对象决定,SessionFactory对象关闭,二级缓存也就消失。

哪些操作会向缓存中读取和存放数据?

测试之前先说一下Hibernate访问数据库,Hibernate对传统的JDBC进行了封装,一般我们访问数据库无非就是增、删、改、查这四个操作,而这四个操作均通过各自的SQL语句完成,所以Hibernate对数据库进行这四个操作时也离开不了SQL语句,如果我们配置Hibernate时配置了show_sql这个属性的话,一旦Hibernate对数据库进行了增、删、改、查操作的话,控制台就会将执行的SQL语句打印出来,这也就是下面我们为什么能将控制台有没有打印SQL语句作为Hibernate有没有访问数据库的原因。下面开始测试:

测试环境:
系统:Windows 10
JDK:1.8.102
Hibernate:5.2.10
软件:MyEclipse 2016

我们先看查询,查询我们常见的有get()、load()以及HQL中的Query对象的list()方法(后面为方便,我们称为query.list())和uniqueResult()方法(后面为方便,我们称为query.uniqueResult())这些方法。

先上结果:
get()、load()均会向缓存中存放以及读取数据,而query.list()和query.uniqueResult()会向缓存存放数据但不会从缓存中读取数据

get()方法:

测试之前先说一下get()方法是怎么查找数据的,它会先到一级缓存中去找,没有的话,它会到二级缓存中继续找,二级缓存中还没有的话,就会立马生成相应的查询SQL,并把这个SQL发送到数据库,到数据库中去找编号为7499的员工,如果数据库中也没找到的话,就会返回null。如果数据库中有的话就会返回查询结果,在它返回查询结果的同时,它还会把查到的结果放进缓存中(一二级缓存都会放)。

测试get()方法代码:

    // get()会向缓存中存放和读取数据测试
    public static void testGetCache(Session session){
        try {
            Employee emp6=session.get(Employee.class, 7499);
            System.out.println(emp6);
            Employee emp7=session.get(Employee.class, 7499);
            System.out.println(emp7);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        employee0_.empno as empno1_0_0_,
        employee0_.ename as ename2_0_0_,
        employee0_.job as job3_0_0_,
        employee0_.sal as sal4_0_0_,
        employee0_.deptno as deptno5_0_0_,
        employee0_.hiredate as hiredate6_0_0_,
        employee0_.comm as comm7_0_0_,
        employee0_.mgr as mgr8_0_0_ 
    from
        SCOTT.Emp employee0_ 
    where
        employee0_.empno=?
员工姓名:ALLEN  员工编号7499
员工姓名:ALLEN  员工编号7499

代码中我们查询编号7499的员工查了2次,但运行结果中只打印了第一次查询的SQL语句,说明第一次是到数据库中查的,第二次没有去数据库中查,但第二次却依然查到了编号7499的员工,原因是因为Hibernate的缓存机制,第一次找到编号为7499的员工到后就会把7499的员工信息放进缓存中,当我们第二次再次查找的7499的员工信息时,由于在缓存中已经找到了,所以就直接返回结果,就不会到数据库中去查了,因此控制台并没有打印第二次的SQL语句。也就是说第二次查询是从缓存中查(读取)出来的。所以结果很明显:get()会向缓存中存放以及读取数据。
get()测试结果:get()会向缓存中存放以及读取数据

load()方法:

load()和get()查找数据的方式差不多,但又有点区别。load()也是先到一级缓存中去找,没找到的话继续到二级缓存中找,二级缓存中还没找到的话,它会返回一个查询结果的代理对象,当后面我们用到查询结果时,这个时候它才会生成相应的SQL语句,并把SQL发送到数据库,到数据库中去找,你后面如果没用到查询结果的话它不会生成查询的SQL,不会到数据库中去找。换句话说,就是你什么时候用到查询结果,他就什么时候去数据库中找,如果在数据库中没找到的话,就会报错,会报org.hibernate.ObjectNotFoundException的异常,导致程序中断。如果数据库中有的话就会返回查询结果,在它返回查询结果的同时,它还会把查到的结果放进缓存中(一二级缓存都会放)

测试load()方法代码:

    // load()会向缓存中存放和读取数据测试
    public static void testLoadCache(Session session){
        try {
          Employee emp6=session.get(Employee.class, 7369);
          System.out.println(emp6);
          Employee emp7=session.get(Employee.class, 7369);
          System.out.println(emp7);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        employee0_.empno as empno1_0_0_,
        employee0_.ename as ename2_0_0_,
        employee0_.job as job3_0_0_,
        employee0_.sal as sal4_0_0_,
        employee0_.deptno as deptno5_0_0_,
        employee0_.hiredate as hiredate6_0_0_,
        employee0_.comm as comm7_0_0_,
        employee0_.mgr as mgr8_0_0_ 
    from
        SCOTT.Emp employee0_ 
    where
        employee0_.empno=?
员工姓名:SMITH  员工编号7369
员工姓名:SMITH  员工编号7369

代码中我们同样查询编号7369的员工查了2次,但运行结果中只打印了第一次查询的SQL语句,说明第一次是到数据库中查的,第二次没有去数据库中查,但第二次却依然查到了编号7369的员工,原因也是因为Hibernate的缓存机制,第一次找到编号为7369的员工后就会把7369的员工信息放进了缓存,第二次查找的7369的员工信息是从缓存中查(读取)出来的。所以load()也会向缓存中存放以及读取数据。
load()方法测试结果:load()会向缓存中存放以及读取数据

query.list()方法

测试query.list()方法代码:

    // 测试query.list()会向缓存中存放数据但不会从缓存的读取数据
    public static void testQueryListCache(Session session) {
        try {
            String hql="from Employee";
            Query query=session.createQuery(hql);
            List<Employee> list=query.list();
            for (int i=0;i<2;i++) {
                System.out.println("员工编号:"+list.get(i).getEmpNo()+"   员工姓名:"+list.get(i).getEmpName());
            }
            System.out.println("------------------------------------------");
            Employee emp=(Employee)session.get(Employee.class, 7654);
            System.out.println("员工编号:"+emp.getEmpNo()+"   员工姓名:"+emp.getEmpName());
            System.out.println("------------------------------------------");
            String hql2="from Employee";
            Query query2=session.createQuery(hql2);
            List<Employee> list2=query2.list();
            for (int i=0;i<2;i++) {
                System.out.println("员工编号:"+list2.get(i).getEmpNo()+"   员工姓名:"+list2.get(i).getEmpName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        employee0_.empno as empno1_0_,
        employee0_.ename as ename2_0_,
        employee0_.job as job3_0_,
        employee0_.sal as sal4_0_,
        employee0_.deptno as deptno5_0_,
        employee0_.hiredate as hiredate6_0_,
        employee0_.comm as comm7_0_,
        employee0_.mgr as mgr8_0_ 
    from
        SCOTT.Emp employee0_
员工编号:20   员工姓名:empSu2
员工编号:19   员工姓名:empSave2
------------------------------------------
员工编号:7654   员工姓名:MARTIN
------------------------------------------
Hibernate: 
    select
        employee0_.empno as empno1_0_,
        employee0_.ename as ename2_0_,
        employee0_.job as job3_0_,
        employee0_.sal as sal4_0_,
        employee0_.deptno as deptno5_0_,
        employee0_.hiredate as hiredate6_0_,
        employee0_.comm as comm7_0_,
        employee0_.mgr as mgr8_0_ 
    from
        SCOTT.Emp employee0_
员工编号:20   员工姓名:empSu2
员工编号:19   员工姓名:empSave2

测试代码中我们用query.list()方法去查询所有员工的编号和姓名查了两次(为了方便看结果,循环打印员工的编号和姓名时只循环了2次),分别在第一条虚线前和第二条虚线后。两条虚线之间我们用get()的方式查找了其中一名员工,看两条虚线间有没有打印get()查询员工的SQL语句来验证第一次query.list()查询后,有没有向缓存中存放数据。

运行结果可以看出,控制台将这两次query.list()查询所有员工的SQL语句都打印了出来,说明两次query.list()查询都是到数据库中查找的。两次query.list()查询之间我们用get()去查询了编号为7654的员工,运行结果显示我们查到了该员工的信息,但控制台却没有打印查询该员工信息的SQL语句,说明get()没有到数据库中去查,get()查到的编号为7654的员工信息不是从数据库中查到的,由于get()查找数据的顺序是先从缓存中找再到数据库中找,但我们用get()却查到了编号为7654的员工,说明get()查到的数据是从缓存中查到(读取到)的,观察整段测试代码,get()查询之前我们只进行了query.list()查询操作,所以很明显缓存中的信息是query.list()查到结果后放进去的,这就说明了query.list()会向缓存中存放数据,此时缓存中已经有了所有员工信息,但get()后面的query.list()查询操作在缓存中已经有要查找的信息时依旧是到数据库中查的,所以会发现query.list()并不会从缓存中查找(读取)数据。综上:测试query.list()会向缓存中存放数据但不会从缓存的读取数据
query.list()测试结果:query.list()会向缓存中存放数据但不会从缓存的读取数据

query.uniqueResult()方法:

测试query.uniqueResult()代码:

    //测试query.uniqueResult()会向缓存中存放数据但不会从缓存的读取数据
    public static void testQueryUniqueResultCache(Session session) {
        try {
            String hql="from Employee where empNo=7876";
            Query query=session.createQuery(hql);
            Employee emp=(Employee)query.uniqueResult();
            System.out.println("员工编号:"+emp.getEmpNo()+"   员工姓名:"+emp.getEmpName());
            System.out.println("-------------------------------------------------");
            //通过get()来测试query.list()有没有向缓存中存放数据
            Employee emp3=(Employee)session.get(Employee.class, 7876);
            System.out.println("员工编号:"+emp3.getEmpNo()+"   员工姓名:"+emp3.getEmpName());
            System.out.println("-------------------------------------------------");
            String hql2="from Employee where empNo=7876";
            Query query2=session.createQuery(hql2);
            Employee emp2=(Employee)query.uniqueResult();
            System.out.println("员工编号:"+emp2.getEmpNo()+"   员工姓名:"+emp2.getEmpName());
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        employee0_.empno as empno1_0_,
        employee0_.ename as ename2_0_,
        employee0_.job as job3_0_,
        employee0_.sal as sal4_0_,
        employee0_.deptno as deptno5_0_,
        employee0_.hiredate as hiredate6_0_,
        employee0_.comm as comm7_0_,
        employee0_.mgr as mgr8_0_ 
    from
        SCOTT.Emp employee0_ 
    where
        employee0_.empno=7876
员工编号:7876   员工姓名:ADAMS
-------------------------------------------------
员工编号:7876   员工姓名:ADAMS
-------------------------------------------------
Hibernate: 
    select
        employee0_.empno as empno1_0_,
        employee0_.ename as ename2_0_,
        employee0_.job as job3_0_,
        employee0_.sal as sal4_0_,
        employee0_.deptno as deptno5_0_,
        employee0_.hiredate as hiredate6_0_,
        employee0_.comm as comm7_0_,
        employee0_.mgr as mgr8_0_ 
    from
        SCOTT.Emp employee0_ 
    where
        employee0_.empno=7876
员工编号:7876   员工姓名:ADAMS

与上面的测试query.list()类似,我们用query.uniqueResult()查编号为7876的员工查了两次,分别是在第一条虚线前和 第二条虚线后。两条虚线中间我们用get()再次查找编号7876的员工,看两条虚线之间有没有打印get()查询的SQL语句来判断query.uniqueResult()有没有向缓存中放入数据。

结果显示两条虚线之外均有打印查询的SQL,说明两次query.uniqueResult()查询都是到数据库中去查的,从运行结果来看,两条虚线之间并没有打印SQL语句,说明我们用get()查找员工7876时并没有到数据库中去查,但是依然查到了员工信息,根据get()查找数据的顺序结合此时get()并没有到数据库中去查,可以知道,get()查到员工信息是从缓存中查(读取)到的,依旧是观察整段测试代码,get()查询员工前面除了query.uniqueResult()查询操作外并没有进行其它任何操作,所以,很显然,缓存中的员工信息是前面query.uniqueResult()查到结果后放进去的。到这就说明了query.uniqueResult()会向缓存中存放数据。
再来,结合get()后面的query.uniqueResult()查询操作,在缓存中已经有要查找的员工时,却还是到数据库中去查找。所以得出结论:query.uniqueResult()不会从缓存的读取数据。
综合就是:query.uniqueResult()会向缓存中存放数据但不会从缓存的读取数据

测试结果:query.uniqueResult()会向缓存中存放数据但不会从缓存的读取数据

说完了查询,我们说添加,添加我们常见的有save()和saveOrUpdate()
先上结果:
save()和saveOrUpdate()进行添加时,均会向缓存中存放数据。

save()方法:

测试save()方法代码

    //测试save()会向缓存中存放数据
    public static void testSaveCache(Session session) {
        try {
            Transaction tr=session.beginTransaction();
            Employee emp1=new Employee();
            emp1.setEmpName("empSave2");
            session.save(emp1);
            System.out.println("添加的对象的主键为:"+emp1.getEmpNo());
            Employee emp2=(Employee)session.get(Employee.class,emp1.getEmpNo());
            System.out.println("员工姓名:"+emp2.getEmpName());
            tr.commit();//只有当这句代码执行时,才会向数据库发送sql
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        hibernate_sequence.nextval 
    from
        dual
添加的对象的主键为:19
员工姓名:empSave2
Hibernate: 
    insert 
    into
        SCOTT.Emp
        (ename, job, sal, deptno, hiredate, comm, mgr, empno) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?)

从代码可以看出,我们在tr.commit();这句代码之前我们又用get()查询了当前添加的对象,此时tr.commit();还没有执行,也就是事务还没提交,所以对象这个时候还没存进数据库,按理说我们应该查不到记录。

但从运行结果中可以看出,我们查到了数据,而且,控制台没有打印相关的查询SQL,说明我们用get()查找的员工信息不是从数据库中找的,结合get()查找数据是先从缓存再到数据库中找的顺序,所以我们能得出,查到的数据是从缓存中查到的,而在我们get()查询之前除了save()操作并没有其它操作,因此,缓存中的数据是save()放的,也就是说在我们调用save()添加时它会把添加的信息放进缓存里。
save()测试结果:save()会向缓存中存放数据

saveOrUpdate()方法:

测试saveOrUpdate()方法代码

    //测试saveOrUpdate()进行添加时会向缓存中存放数据
    public static void testSaveOrUpdateCache(Session session) {
        try {
            Transaction tx=session.beginTransaction();
            Employee emp3=new Employee();
            emp3.setEmpName("empSu2");
            session.saveOrUpdate(emp3);
            System.out.println("添加的对象的主键为:"+emp3.getEmpNo());
            Employee emp4=(Employee)session.get(Employee.class,emp3.getEmpNo());
            System.out.println("员工姓名:"+emp4.getEmpName());
            tx.commit();//只有当这句代码执行时,才会向数据库发送sql
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }
    }

运行结果:

Hibernate: 
    select
        hibernate_sequence.nextval 
    from
        dual
添加的对象的主键为:20
员工姓名:empSu2
Hibernate: 
    insert 
    into
        SCOTT.Emp
        (ename, job, sal, deptno, hiredate, comm, mgr, empno) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?)

和测试save()一样,我们在tx.commit();这句代码之前我们又用get()查询了当前添加的对象,此时tr.commit();还没有执行,也就是事务还没提交,所以对象还没存进数据库,按理说我们应该查不到记录。

运行结果也和上面save()的一样,控制台并没有打印相关的查询SQL说明,说明我们用get()查找的员工信息不是从数据库中找的,结合get()查找数据是先从缓存再到数据库中找的顺序,所以我们能得出,查到的数据是从缓存中查到的,而在我们get()查询之前除了saveOrUpdate()操作并没有其它操作,因此,缓存中的信息是saveOrUpdate()操作放进去的,所以,saveOrUpdate()也会向缓存中存放数据。
saveOrUpdate()测试结果:saveOrUpdate()会向缓存中存放数据

以上均为个人理解,如有错误,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值