首页 > 代码库 > Hibernate 缓存介绍

Hibernate 缓存介绍

hibernate缓存包括一级缓存,二级缓存以及查询缓存

一级缓存

一级缓存是Session的缓存,由于session对象的生命周期对应于一个事务,所有session的缓存是事务范围的缓存。第一级缓存是必须的,缓存中的每个持久类对象都有唯一的OID。无需做任何配置,hibernate自动维护。

当执行load/get/list/iterator/ filter/save/update/saveOrUpdate等方法时会把得到的实体对象放入一级缓存中(只支持实体对象缓存,不支持属性的缓存),当在同一个session范围内执行load/get/iterator等查询语句查询缓存过的数据时(不包括list),Hibernate不会去数据库中查询。

1.load

在执行session.load时,Hibernate首先从当前session的一级缓存中获取id对应的值,在获取不到的情况下,将根据该对象是否配置了二级缓存来做相应的处理,如配置了二级缓存,则从二级缓存中获取id对应的值,如仍然获取不到则还需要根据是否配置了延迟加载来决定如何执行,如未配置延迟加载则从数据库中直接获取,在从数据库获取到数据的情况下,Hibernate会相应的填充一级缓存和二级缓存,如配置了延迟加载则直接返回一个代理类实例,只有在访问实例时(get)才进行数据库查询的操作。

2.get

session.get与session.load的行为基本一致,只是当缓存中没有需要的对象时,session.get会直接从数据库加载数据(session.load返回代理类实例)。

3.Query.list

list取到结果集后会填充一级、二级和查询缓存,但是list方法查询数据时不会使用一级缓存和二级缓存的值,除非配置了查询缓存

4.Query.iterator

在执行iterator时,先向数据库发起的是select id from这样的语句,获取符合查询条件的id,之后在进行iterator.next调用时才会根据id查看缓存中是否存在数据,如果缓存中没有会发起session.load的调用获取实际的数据。所有最理想状态下iterator会发出一条查询id的sql语句,但在最差的情况下会发出N+1条查询语句。

 

一级缓存管理常用api

evit(obj) : 将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象. 

clear() : 将一级缓存中的所有持久化对象清除,释放其占用的内存资源 
contains(obj): 判断指定的对象是否存在于一级缓存中. 
flush() :刷新一级缓存区的内容,使之与数据库数据保持同步. 

因为save方法支持缓存,大批量数据添加时每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就手动清空缓存。

 1 for (int i=0; i<100; i++) { 2     Student student = new Student(); 3     student.setName("student" + i); 4     session.save(student); 5     //20条更新一次 6     if (i % 20 == 0) { 7         //清空缓存前将缓存中的数据与数据库同步 8         session.flush(); 9         //清空缓存10         session.clear();11     }12 }13     

 

二级缓存

二级缓存需要sessionFactory来管理,它是进程级的缓存,所有是session共享的,二级缓存比较复杂,一般用第三方产品,官方推荐使用ehcache。

配置ehcache二级缓存

1.添加jar包

1 <dependency>2       <groupId>org.hibernate</groupId>3       <artifactId>hibernate-ehcache</artifactId>4       <version>4.3.0.Final</version>5  </dependency>

2.Hibernate配置文件(hibernate4.0,不同版本hibernate下provider_class会有不同)

1 <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>2 <!-- 默认即为true -->3 <property name="hibernate.cache.use_second_level_cache">true</property>

3.ehcache配置文件ehcache.xm(根路径下)

 1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache> 3     <defaultCache  4         maxElementsInMemory="50000"  5         eternal="false"  6         overflowToDisk="true"  7         timeToIdleSeconds="3600"  8         timeToLiveSeconds="3600"  9            /> 10     11     <cache name="com.zlt.hibernatedemo.Student" 12         maxElementsInMemory="50000" 13          eternal="true" 14          />15 </ehcache>

4.类添加二级缓存功能

方法一:配置hibernate.cfg.xml

<class-cache usage="read-only" class="com.zlt.hibernatedemo.Student"/>

方法二:配置hbm文件

在<id>属性前加入<cache usage="read-only"/>

 

二级缓存缓存的key为id,value为实体对象。一般load(),iterate()使用到二级缓存,list()需要结合查询缓存使用,二级缓存在数据发生任何变化(新增、更新、删除)的情况下都会自动的被更新。

二级缓存管理常用api

session.setCacheMode(CacheMode.IGNORE):禁止使用二级缓存

evict(Class arg0, Serializable arg1): 将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源. 
例如sessionFactory.evict(Student.class, new Integer(1));  

evict(Class arg0)  将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源. 
例如sessionFactory.evict(Student.class);  

evictCollection(Stringarg0) 将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源. 
例如sessionFactory.evictCollection("Grade.students"); 

 
查询缓存

查询缓存是专为Query的list方法设计的。对于iterate()方法,无论是查询对象属性还是对象本身,查询缓存用与不用都没有区别

配置查询缓存

1.查询缓存必须要在hibernate.cfg.xml中显示启用:<property name="hibernate.cache.use_query_cache">true</property>

2.在代码中如果要用到查询缓存(无论是写还是读缓存),都要进行开启操作,可通过Query的setCacheable(true)方法开启;

 

查询缓存的生命周期与Session无关(可以跨Session查询),当查询关联的表发生改变,那么查询缓存的生命周期结束(delete、update、modify)

开启查询缓存,并用Query查询对象的属性(可以是一个或多个)时,采用Query的list方法可以把得到的属性集合写入查询缓存中。如果查询缓存已经有了该对象的属性,那么就不会发出SQL而直接从查询缓存中取出来;

 

二级缓存和查询缓存关系

1.关闭查询缓存,开启二级缓存时: 
第二次查询属性时iterate只会发出获取id列表的sql,list会发出和第一次一样的请求实体的sql。 

2.开启查询缓存,关闭二级缓存

如果开启查询缓存并通过list接口查询对象,在首次查询时会发出SQL从数据库中获取对象,同时将对象的id列表放入查询缓存中;如果再次用查询缓存查询对象,则会根据该对象的id(session.load)发出SQL从数据库中加载对象(这时会发出N条SQL语句)

 

3.开启查询缓存,开启二级缓存 
第二次查询属性时iterate只会发出获取id列表的sql,list不发sql(此时的list接口有了读二级缓存的能力) 
以上说明iterate只和二级缓存有关,list和二级缓存和查询缓存都有关。