Hibernate中用到了缓存的概念,那么什么是缓存呢?这里介绍的缓存并不是指计算机的内存或者CPU的一二级缓存,这里的缓存是指为了降低程序对物理数据源访问的频次,从而提高程序运行性能的一种策略。
@Test
public void testCache() {
Session session = getSession();
Employee emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
}
我们使用JUnit来进行测试,其中getSession()方法是一个获取Session对象的工具方法,在这个例子中我们首先获取Session,然后查询empid为1的对象,接着再做一次查询,仍然查询empid为1的对象,我们运行测试方法,查看控制台的输出。
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?
张三
张三
可以看到控制台只打印了一次查询的SQL,这说明用Session第一次查询到empid为1的对象之后这个对象被缓存起来了,第二次查询的时候直接从缓存中获取相应的对象,而不是再查一次数据库。这个例子表明Hibernate的缓存是与Session有关的。
@Test
public void testCache() {
Session session = getSession();
Employee emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
session = getSession();
emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
}
在这个例子中我们使用了两个Session来查询同一个对象,运行程序,查看控制台输出。
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?
张三
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?
张三
我们发现当我们使用不同的Session查询同一个对象时,控制台打印了两次查询SQL,与上面一个例子结合起来我们发现,同一个Session第二次访问同一个对象将使用缓存,而在不同的Session中多次查询同一个对象时,会执行多次数据库查询。
@Test
public void testCache() {
Session session = getSession();
Employee emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
session.evict(emp);
emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
}
使用clear()方法就是将所有的对象都从缓存中移除,这样下次再查询时所有对象都要重新从数据库中查找。
@Test
public void testCache() {
Session session = getSession();
Query query = session.createQuery("from Employee");
List list = query.list();
for(Employee emp : list) {
System.out.println(emp.getEmpname());
}
list = query.list();
for(Employee emp : list) {
System.out.println(emp.getEmpname());
}
}
运行程序,观察控制台输出,控制台打印如下信息。
Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_
张三
李四
Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_
张三
李四
我们发现打印了两次SQL,这说明Query.list()方法不会使用缓存,也就是说每次查询时都要从数据库中重新查询数据。如果我们使用Query.iterate()方法会怎样呢?
@Test
public void testCache() {
Session session = getSession();
Query query = session.createQuery("from Employee");
List list = query.list();
for(Employee emp : list) {
System.out.println(emp.getEmpname());
}
Iterator it = query.iterate();
while(it.hasNext()) {
System.out.println(it.next().getEmpname());
}
}
运行程序观察控制台输出。
Hibernate: select employee0_.empid as empid1_0_, employee0_.empname as empname2_0_ from employee employee0_
张三
李四
Hibernate: select employee0_.empid as col_0_0_ from employee employee0_
张三
李四
我们发现在使用Query,list()方法查询到所有数据之后,在使用Query.iterate()方法时只是去查询了empid的信息,我们再次修改上面的例子,只使用Query.iterate()进行查询。
@Test
public void testCache() {
Session session = getSession();
Query query = session.createQuery("from Employee");
Iterator it = query.iterate();
while(it.hasNext()) {
System.out.println(it.next().getEmpname());
}
}
控制台打印如下输出:
Hibernate: select employee0_.empid as col_0_0_ from employee employee0_
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?
张三
Hibernate: select employee0_.empid as empid1_0_0_, employee0_.empname as empname2_0_0_ from employee employee0_ where employee0_.empid=?
李四
Hibernate首先去数据库查询empid信息,然后又根据empid信息去数据库中查询了相应的数据。结合上面的例子我们其实可以发现Query.iterate()其实是用到缓存了的,它首先将所有对象的id查询出来,然后根据id到缓存中将所有对象都查询出来,如果缓存中没有数据,则把对象从数据库中一条一条的查出来。
net.sf.ehcache
ehcache
2.10.2
org.hibernate
hibernate-ehcache
5.2.3.Final
接下来我们需要在hibernate.cfg.xml中添加相应的配置,我们需要添加如下配置。
true
org.hibernate.cache.ehcache.EhCacheRegionFactory
net.sf.ehcache.hibernate.EhCacheProvider
在这里需要注意一下Hibernate 4.0及以后的需要按照上面的配置,而Hibernate 3.3 的配置是这样的:
true
org.hibernate.cache.EhCacheProvider
如果在Hibernate4.0中仍按照Hibernate3.3进行配置的话运行程序会报错
org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given, please either disable second level cache or set correct region factory class name to property
然后我们需要添加ehcache.xml配置文件,这个配置文件需要放在src目录下。
最后我们需要在需要缓存的表对应的映射文件中添加
其中usage属性指定缓存策略,可选的策略包括transactional,read-only,read-write和nonstrict-read-write;region属性指定二级缓存区域名,指定了二级缓存区域名之后就可以在ehcache.xml中配置相应的自定义缓存了;include表示指定是否缓存延迟加载的对象,all表示缓存所有对象,non-lazy表示不缓存延迟加载的对象。
@Test
public void testCache2() {
Session session = getSession();
Employee emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
session = getSession();
emp = session.get(Employee.class, 1);
System.out.println(emp.getEmpname());
}
运行程序,与一级缓存不同的是当使用不同的Session查询同一对象时不再多次查询数据库,而是直接使用缓存。