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