说到hibernate中的缓存我们了解的比较多的是一级和二级缓存,通过前面两篇博客的总结我们知道一级和二级缓存主要是缓存查询的实体或实体集的,那么如果我们是通过HQL或SQL语句查询普通的属性,这种情况是否存在缓存?答案是肯定的这种缓存就是查询缓存。如果返回的是实体那么查询缓存只会将实体的id缓存下来。查询缓存的生命周期比较难以确定,官方的定义是“当关联的表发生修改,查询缓存的生命周期结束”。自己对这句话不是特别理解。但是可以确定和session和sessionFactory都没有关系。
查询缓存的配置比价简单,需要在hibernate.cfg.xml中配置
<propertyname="hibernate.cache.use_query_cache">true</property>
在程序中需要使用session.setCacheable(true)进行显示的调用开启查询缓存,下面我们来看几个实例,由于get和load是查询实体的所以我们只测试list和iterate。
/**
* 开启查询,关闭二级缓存,采用query.list()查询普通属性
*
* 在一个session中发query.list()查询
*/
public void testList1() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Listnames = session.createQuery("select s.namefrom Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size();i++) {
Stringname = (String)names.get(i);
System.out.println(name);
}
System.out.println("-------------------------------------------------------");
//不会发出查询语句,因为启用查询缓存
names= session.createQuery("select s.namefrom Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size();i++) {
Stringname = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
以上代码块执行只发出一条查询语句,可见开启查询缓存对查询普通属性是有作用的。
/**
* 开启查询,关闭二级缓存,采用query.list()查询普通属性
*
* 在两个session中发query.list()查询
*/
public void testCache2() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Listnames = session.createQuery("select s.namefrom Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size();i++) {
Stringname = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session= HibernateUtils.getSession();
session.beginTransaction();
//不会发出查询语句,因为查询缓存和session的生命周期没有关系
Listnames = session.createQuery("select s.namefrom Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size();i++) {
Stringname = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
} <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
以上代码块是在两个session中进行普通属性的查询,但是也只发出一条查询语句,可见查询缓存的生命周期是有它的特殊性。
/**
* 开启查询,关闭二级缓存,采用query.list()查询实体
*
* 在两个session中发query.list()查询
*/
public void testCache5() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Liststudents = session.createQuery("select s fromStudent s")
.setCacheable(true)
.list();
for (int i=0;i<students.size(); i++) {
Studentstudnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Liststudents = session.createQuery("select s fromStudent s")
.setCacheable(true)
.list();
for (int i=0;i<students.size(); i++) {
Studentstudnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
需要注意这个实例测试时需要在hib.cfg.xml中发二级缓存关闭,我们发现执行的结果为第一次查询发出一条语句查询到所有的学生实体,但是第二次查询时发出了N调语句,这是因为第一次返回结构后查询缓存将实体的id缓存下来,第二次执行query.list(),将查询缓存中的id依次取出,分别到一级缓存和二级缓存中查询相应的实体,如果缓存中存在就使用缓存中的实体对象,否则根据id发出查询学生的语句。
小结:
1、查询缓存缓存普通属性,如果返回的结果是实体则缓存id。
2、在一级缓存,二级缓存和查询缓存都打开的情况下作查询操作时这样的:查询普通属性,会先到查询缓存中取,如果没有,则查询数据库;查询实体,会先到查询缓存中取id,如果有,则根据id到缓存(一级/二级)中取实体,如果缓存中取不到实体,再查询数据库。