Hibernate中存在三种缓存:
1:一级缓存:Session级别的缓存,load()、Iterate()方法读取数据的时候就会先去查询这个级别的缓存。而list()方法不会。所以连续用list()方法查询两次,会发出两条SQL语句,但是如果连续用iterate()方法查询,只会发出一条SQL语句,第二次会去session的缓存中去查询数据。
list()方法测试代码:
public void testListQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
List<Category> list1 = session.createQuery("from Category").list();
for(int i = 0 ;i < list1.size();i ++){
System.out.println(list1.get(i).getName());
}
List<Category> list2 = session.createQuery("from Category").list();
for(int i = 0 ;i < list2.size();i ++){
System.out.println(list2.get(i).getName());
}
session.getTransaction().commit();
session.close();
}
运行结果如下:
Hibernate:
select
category0_.id as id0_,
category0_.name as name0_
from
Category category0_
c0
c1
Hibernate:
select
category0_.id as id0_,
category0_.name as name0_
from
Category category0_
c0
c1
如上所述,发出了两条SQL,当把list方法缓存iterate方法就只发出一条sql语句。
Iterate()方法测试代码:
@Test
public void testIterateQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Iterator<Category> list1 = session.createQuery("from Category").iterate();
while(list1.hasNext())
{
Category c = list1.next();
System.out.println(c.getName());
}
Iterator<Category> list2 = session.createQuery("from Category").iterate();
while(list2.hasNext())
{
Category c = list2.next();
System.out.println(c.getName());
}
session.getTransaction().commit();
session.close();
}
运行结果如下所示:
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
Hibernate:
select
category0_.id as id0_0_,
category0_.name as name0_0_
from
Category category0_
where
category0_.id=?
c0
Hibernate:
select
category0_.id as id0_0_,
category0_.name as name0_0_
from
Category category0_
where
category0_.id=?
c1
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
c0
c1
我们发现,用Iterate()方法两次查询,虽然会发出两条SQL语句去数据库取ID值,但是真正在取数据的时候,还是使用的session级别的缓存,没有去重复查询数据库。但是list是第二次直接查询了数据库。
2:二级缓存:sessionFactory级别的缓存,可以跨session存在,首先要回答问什么要存在二级缓存这个问题,举个例子,有一百用户,对应一百个session,那么如果一百个session从数据库中取出的是完全相同的数据,如果此时使用session级别的缓存,内存岂不是被极大浪费掉了。如果使用sessionFactory级别的缓存,一般而言,一个项目只有一个sesisonFactory,那么此时就可以节省掉99%的内存空间。一般来说,在整个项目中,我们会把很少修改、数据有限、常访问的数据放到sessionFactory级别的缓存中。在常用的方法中,要注意一点。load(),iterate()方法默认会使用二级缓存,list()默认会往二级缓存中加入数据,但是不会使用二级缓存。
弄清楚了二级缓存是什么东西,那么在hibernate中如何配置二级缓存呢?这就要分两步走了。
第一步:在hibernate.cfg.xml中开启二级缓存,并设置二级缓存的提供类。(hibernate默认提供的二级缓存在实际开发中不推荐使用,一般使用第三方的二级缓存,这里使用Ehcache,所以要导入Ehcache的相关jar包和Ehcahe自己的配置文件ehcache.xml,而且由于Ehcahe的日志是使用的apache的Commons-logging,和hibernate使用的log4g不一样,所以这个日志包也要单独导入到项目中。)如下所示:
<property name= "cache.use_second_level_cache">true</property>
<property name="cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>
第二步:将需要使用二级缓存的类用@Cache注解进行注释,这样程序就知道此类的实例是要放到二级缓存的,本例将Category类进行注释。
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Category {
private int id;
private String name;
private List<Topic> topics;
下面来对其进行测试:
测试list是否使用二级缓存代码:
@Test
public void testListQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
List<Category> list1 = session.createQuery("from Category").list();
for(int i = 0 ;i < list1.size();i ++){
System.out.println(list1.get(i).getName());
}
session.getTransaction().commit();
session.close();
Session session2 = this.sessionFactory.openSession();
session2.beginTransaction();
List<Category> list2 = session2.createQuery("from Category").list();
for(int i = 0 ;i < list2.size();i ++){
System.out.println(list2.get(i).getName());
}
session2.getTransaction().commit();
session2.close();
}
输出如下:(我们发现list()两次session都到数据库取数据了,没有使用二级缓存)
Hibernate:
select
category0_.id as id0_,
category0_.name as name0_
from
Category category0_
c0
c1
Hibernate:
select
category0_.id as id0_,
category0_.name as name0_
from
Category category0_
c0
c1
当把上面list方法换成iterate方法后结果如下(此处不给Iterate的代码了),可以看到确实使用了二级缓存:
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
Hibernate:
select
category0_.id as id0_0_,
category0_.name as name0_0_
from
Category category0_
where
category0_.id=?
c0
Hibernate:
select
category0_.id as id0_0_,
category0_.name as name0_0_
from
Category category0_
where
category0_.id=?
c1
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
c0
c1
3:三级缓存(查询缓存):前面说过,list查询不使用二级缓存,因为查询条件每次可能不一样,那么如果使用list进行两次查询的时候,如果两次查询条件相同,这个时候,可不可以使用缓存呢?答案是肯定的,这就是我们在hibernate中常常说的查询缓存。
查询缓存实质:将特定的查询语句(查询条件不变)与缓存的数据建立了一个对应关系。但是注意,因为查询缓存依赖于二级缓存,所以,要使用查询缓存之前,必须先打开二级缓存。
那么如何配置让程序使用二级缓存呢?也分两步走:
第一步:配置hibernate.cfg.xml使用查询缓存:
<property name="cache.use_query_cache">true</property>
第二步:调用Query对象的setCachable(true)来让查询使用查询缓存策略。测试代码如下:
@Test
public void testListQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
List<Category> list1 = session.createQuery("from Category").setCacheable(true).list();
for(int i = 0 ;i < list1.size();i ++){
System.out.println(list1.get(i).getName());
}
session.getTransaction().commit();
session.close();
Session session2 = this.sessionFactory.openSession();
session2.beginTransaction();
List<Category> list2 = session2.createQuery("from Category").setCacheable(true).list();
for(int i = 0 ;i < list2.size();i ++){
System.out.println(list2.get(i).getName());
}
session2.getTransaction().commit();
session2.close();
}
测试结果如下:(虽然我们此时使用list方法,但是由于我们使用了查询缓存,所以此时不用查询两次数据库。)
select
category0_.id as id0_,
category0_.name as name0_
from
Category category0_
c0
c1
c0
c1