Hibernate3的缓存机制



Hibernate3的缓存机制.

Hibernate的缓存机制
缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的次数,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。

缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期。

Hibernate在查询数据时,首先到缓存中去查找,如果找到就直接使用,找不到的时候就会从物理数据源中检索,所以,把频繁使用的数据加载到缓存区后,就可以大大减少应用程序对物理数据源的访问,使得程序的运行性能明显的提升.

Hibernate缓存分类:一级缓存、二级缓存和查询缓存;
一级缓存也叫session级缓存或事务级缓存,一级缓存的生命周期和Session的生命周期一致,也就是当 session关闭时缓存即被清除,一级缓存在Hibernate中是不可配置的,即不能被卸载。
二级缓存也称进程级缓存或SessionFactory级缓存,二级缓存可以被所有的session共享,二级缓存的生命周期和SessionFactory的生命周期一致。二级缓存在Hibernate中是可以配置的,可以通过class-cache配置类粒度级别的缓存(class-cache在class中数据发生任何变化的情况下自动更新),同时也可通过collection-cache配置集合粒度级别的缓存(collection-cache仅在 collection中增加了元素或者删除了元素的情况下才自动更新,也就是当collection中元素发生值的变化的情况下它是不会自动更新的)。
查询缓存,查询的结果集也可以被缓存。只有当经常使用同样的参数进行查询时,这才会有些用处。要使用查询缓存,首先你必须打开它: hibernate.cache.use_query_cache true。该设置将会创建两个缓存区域 – 一个用于保存查询结果集(org.hibernate.cache.StandardQueryCache); 另一个则用于保存最近查询的一系列表的时间戳(org.hibernate.cache.UpdateTimestampsCache)。 请注意:在查询缓存中,它并不缓存结果集中所包含的实体的确切状态;它只缓存这些实体的标识符属性的值、以及各值类型的结果。 所以查询缓存通常会和二级缓存一起使用。绝大多数的查询并不能从查询缓存中受益,所以Hibernate默认是不进行查询缓存的。如若需要进行缓存,请调用 Query.setCacheable(true)方法。这个调用会让查询在执行过程中时先从缓存中查找结果, 并将自己的结果集放到缓存中去。 查询缓存在Hibernate同样是可配置的,默认是关闭的,查询缓存的配置:
在hibernate.cfg.xml文件中启用查询缓存,如:
<property name=”hibernate.cache.use_query_cache”>true</property>
在程序中必须手动启用查询缓存,如:
query.setCacheable(true);

缓存自然会带来并发的访问问题,这个时候相应的就要根据应用来设置缓存所采用的事务隔离级别,和数据库的事务隔离级别概念基本一样,没什么多介绍的。

缓存的实现通过是通过key/value的Map方式来实现,在 Hibernate的一级、二级和查询缓存也同样如此,一级、二级缓存使用的key均为po的主键ID,value即为po实例对象,查询缓存使用的则为查询的条件、查询的参数、查询的页数,value有两种情况,如果采用的是select po.property这样的方式那么value为整个结果集,如采用的是from这样的方式那么value为获取的结果集中各po对象的主键ID,这样的作用很明显,节省内存。

简单介绍完Hibernate的缓存后,再结合Hibernate的获取数据方式来说明缓存的具体使用方式,在Hibernate中获取数据常用的方式主要有四种:Session.load、Session.get、Query.list、Query.iterator。
1、Session.load
在执行session.load时,hibernate认为该id对应的对象在数据库中一定存在。load方法在创建时首先会查询session,看看该id对应的对象是否存在,不存在则创建代理,实际用到该对象中的其他属性时才查询数据库,万一数据库中不存在该记录,才抛出异常。Load方法抛出异常是指在使用该对象的数据时,而不是在创建该对象的数据时。
一级缓存的管理:
evit(Object obj) 将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象.
clear() 将一级缓存中的所有持久化对象清除,释放其占用的内存资源
contains(Object obj) 判断指定的对象是否存在于一级缓存中.
flush() 刷新一级缓存区的内容,使之与数据库数据保持同步.
如何避免一次性大量实体数据入库导致内存溢出:先flush,然后clear
二级缓存的管理:
evict(Class arg0) 将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源.
evict(Class arg0, Serializable arg1) 将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源.
evictCollection(String arg0) 将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源.
2、Session.get
在执行Session.get时,hibernate会确认一下该id对应的数据是否存在。get方法首先在session缓存中查询,没有的话然后在二级缓存中查询,最后查询数据库。
3、Query.list
在执行Query.list时,Hibernate的做法是首先检查是否配置了查询缓存,如配置了则从查询缓存中查找key为查询语句+查询参数+分页条件的值,如获取不到则从数据库中进行获取,从数据库获取到后Hibernate将会相应的填充一级、二级和查询缓存,如获取到的为直接的结果集,则直接返回,如获取到的为一堆id的值,则再根据id获取相应的值(Session.load),最后形成结果集返回,可以看到,在这样的情况下,list也是有可能造成N次的查询的。
查询缓存在数据发生任何变化的情况下都会被自动的清空。
4、Query.iterator
在执行Query.iterator时,和Query.list的不同的在于从数据库获取的处理上,Query.iterator向数据库发起的是 select id from这样的语句,也就是它是先获取符合查询条件的id,之后在进行iterator.next调用时才再次发起session.load的调用获取实际的数据。
可见,在拥有二级缓存并且查询参数多变的情况下,Query.iterator会比Query.list更为高效。

http://blog.csdn.net/switzerland/article/details/3343348