缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
一级缓存,Session级共享。
save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。
随着sesssion的关闭session缓存就关闭了
看到hql,sql语句就不能从一级缓存中取,例如Query
-------------------------------------------------------------------------------------------------------
二级缓存,SessionFactory级共享。
·实现为可插拔,通过修改cache.provider_class参数来改变;
hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持。可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。
·在hibernate.cfg.xml中加入:
<class-cache class="className" usage="read-only"/>
或在映射文件的class元素加入子元素:
<cache usage="read-write"/>
其中usage:
read-only(只读 )
read-write(严格读写,多线程时会做同步 )
nonstrict-read-write(不严格的读写 )
Transactional(事务性缓存 )
·Session的save(这个方法不适合native生成方式的主键),
update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,但只有(没打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)。
·Query,Criteria(查询缓存)由于命中率较低,所以hibernate默认是关闭;修改cache.use_query_cache为true打开对查询的缓存,并且调用query.setCacheable(true)或criteria.setCacheable(true)。
·SessionFactory中提供了evictXXX()方法用来清除缓存中的内容。例:HibernateUtil.getSessionFactory().evict(User.class);
·统计信息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息。
例子:
Hibernate采用第三方缓存控制,我们使用oscache来演示首先需要加入oscache2.1.jar这个库文件,在Hibernate3.3.2中lib文件夹下的optional文件夹内。
另外需要加入oscache的配置文件,在Hibernate3.3.2/porject/etc下找到oscache.proprities文件,黏贴到工程src目录下即可
我用的是Hibernate3.3.2,还需要加载common-logging-1.1.jar这个库,因为oscache使用的日志框架是common-logging。
添加完以后我们修改hibernate.cfg.xml配置文件:加入
<!-- 二级缓存 --> <property name="cache_user_second_level_cache">true</property> <!-- 使用二级缓存 --> <property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property> <!--使用oscache-->
另外我们还需要指定哪些类需要采用缓存机制,以Users为例
修改Users.hbm.xml配置(还可以在hibernate.cfg.xml中配置:<class-cache class="domain.Users(全类名)" usage="read-only"/>)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="domain"> <class name="Users"> <cache usage="read-write"/> <id name="id"> <generator class="native"/> </id> <property name="name"></property> <property name="birthday"></property> </class> </hibernate-mapping>
下面输出一下oscache的统计信息看看,即cache的命中率,当然打开统计信息消耗不少的资源(开发测试时可能要看)
hibernate.cfg.xml中加入
<!-- 打开二级缓存统计信息 --> <property name="generate_statistics">true</property>
程序代码中:
Statistics st = HibernateUtil.getSessionFactory().getStatistics(); System.out.println(st); System.out.println("put:" + st.getSecondLevelCachePutCount());//加入二级缓存次数 System.out.println("hit:" + st.getSecondLevelCacheHitCount());//命中次数 System.out.println("miss:" + st.getSecondLevelCacheMissCount());//没有命中次数
-------------------------------------------------------------------------------------------------------------
·分布式缓存和中央缓存。
大型的系统往往是一台数据库,多台web服务器,那么如何协调好多台web服务器上的缓存同步就会存在很大的问题。
比如u1 u2 两个用户分别访问w1 w2 两台服务器,同时读取了user对象信息,那么两台服务器上的数据就会分别保存在w1 w2的缓存上,如果u1更新了user的信息,那么w2的缓存依然还是没更新前的数据,u2读到的数据不是正确的数据。
这种分布式缓存对更新的成本比较高
另外是把缓存独立出来,w1 w2共用一台缓存服务器(中央缓存),那么更新方面的问题是解决了,但是读取数据的开销也大了,不再是本地缓存读取,需要连接缓存服务器读取了。
·使用缓存的条件
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。