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()会向缓存中存放数据
以上均为个人理解,如有错误,欢迎指正