Hibernate旅程(七)Hibernate缓存机制–一级缓存
最后更新于:2022-04-01 14:49:59
Hibernate一级缓存
缓存就是你去小卖铺买东西,不用再去生产车间里买东西,当小卖铺倒闭了,也就是session缓存生命周期结束。hibernate一级缓存的声明周期很短,和session的生命周期一致,hibernate的一级缓存也叫做session级缓存,或叫事务级缓存。下面来看session控制的一级缓存。
### 同一session中使用两次load()进行查询。
代码入下所示,我们在同一个session中两次调用load()。
~~~
/**
* 在同一个session中发出两次load查询
*/
public voidtestCache1() {
Sessionsession = null;
try {
//使用load查询两遍.
session= HibernateUtils.getSession();
session.beginTransaction();
Studentstudent =(Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不会发出查询语句,load使用缓存,在同一个session中.
student =(Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
~~~
两次都采用load()进行加载并打印出学生的名字。发出的sql语句如下所示。对于loadlazy加载,只有在使用load加载上来的类,才真正的去数据库查询。控制台打印的sql代码如下所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908f9b4564.jpg)
从显示结果中我们可以看出,在第一次真正使用load的时候,发出sql语句。Hibernate会把查询上来真实的对象放到session的map中。当第二次load再次使用的时候,不会再发送sql语句,而是直接从session的缓存中取出。
### 同一session中使用两次get()进行查询。
源代码也就是把上述load换成get。
Get和load的区别,get不支持延迟加载而load支持延迟加载,但当我们在同一次访问中访问两次get方法时,可以看到当加载get方法的时候控制台立刻打印sql,同时放到了缓存中。当我们第一次调用get方法的时候,同样和load方法一样,没有再次去数据库中查询,而是直接从缓存中取出来显示。打印结果如下图所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908f9c95a6.jpg)
### 在同一session中发出两次iterate查询,查询实体对象。
代码如下所示。
~~~
/**
* 在同一个session中发出两次iterate查询,查询实体对象
* 发出两次迭代查询.查询实体对象.
*/
public void testCache3() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Iteratoriter = session.createQuery("from Student s where s.id<5").iterate();
while(iter.hasNext()) {
Studentstudent = (Student)iter.next();
System.out.println(student.getName());
}
System.out.println("--------------------------------------");
//它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存
iter= session.createQuery("from Student s where s.id<5").iterate();
while(iter.hasNext()) {
Studentstudent = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
~~~
运行结果如下所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908f9e5afd.jpg)
第一次调用迭代器的查询时,首先发出查询id的语句,并根据id查询学生。当第二次调用时,只会发出查询id的语句,不会再根据id来查询对应的学生对象。这说明iterate(迭代器)是支持缓存的。
### 在同一session中发出两次iterate查询,查询实体对象。
代码如下所示。
~~~
Session session = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Iteratoriter = session.createQuery("select s.name from Student s wheres.id<5").iterate();
while(iter.hasNext()) {
Stringname = (String)iter.next();
System.out.println(name);
}
System.out.println("--------------------------------------");
//iterate查询普通属性,一级缓存不会缓存,所以发出查询语句
//一级缓存是缓存实体对象的
iter= session.createQuery("select s.name from Student s wheres.id<5").iterate();
while(iter.hasNext()) {
Stringname = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
~~~
显示结果如下所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908fa10478.jpg)
根据显示结果可知,迭代器查询普通属性,一级缓存不会存储,所以当第二次查询的时候仍然发出查询语句。这说明iterate一级缓存缓存的是实体对象,对于普通属性不会缓存。
在两个session中发出load查询。
代码如下所示。
~~~
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = (Student)session.load(Student.class, 1);
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = (Student)session.load(Student.class, 1);
//会发出查询语句,session间不能共享一级缓存数据
//因为他会伴随着session的消亡而消亡
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
~~~
显示结果如下所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908fa2e6e2.jpg)
从上图可以看出,会发出两次sql,这也说明了session之间不能共享一级缓存数据,因为缓存会本随着自己的那个session的消亡而消亡。
### 在一个session中先调用save(),再调用get或load查询刚刚save的数据。
代码如下所示。
~~~
/**
* 在同一个session中先调用save,再调用load查询刚刚save的数据
*/
public voidtestCache6() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = new Student();
student.setName("张三");
Serializableid = session.save(student);
student= (Student)session.load(Student.class,id);
//不会发出查询语句,因为save支持缓存
System.out.println("student.name=" +student.getName());
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
~~~
显示结果如下所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908fa46506.jpg)
从图中可以看出,只发送一次插入语句,当我们再次查询的时候,没有去数据库进行查询,这说明当使用session.save()时,已经放入缓存中。再进行查询时会从缓存中取出。
### 大批量数据的添加。
代码如下所示。
~~~
public voidtestCache7() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
for (int i=0;i<100; i++) {
Studentstudent = newStudent();
student.setName("张三" +i);
session.save(student);
//每20条更新一次
if (i %20 == 0) {
session.flush();
//清除缓存的内容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exceptione) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
~~~
打印sql如下图所示。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-21_576908fa5c8c0.jpg)
从图中可知,打印了100条inset语句,在一个session中缓存100条数据很大,我们可以设置每20条清空缓存。
### Hibernate一级缓存总结
从上可知,load、get、iterate查询实体对象时,支持一级缓存,但查询普通属性时不支持一级缓存,当我们大批量数据插入或更新时,由于缓存中数据量太大,我们可以设置缓存中的条数,使用session.clear()来清除缓存。