与Session相对的是,SessionFactory也提供了相应的缓存机制。SessionFactory缓存可以依据功能和目的的不同而划分为内置缓存和外置缓存。 SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的副本,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来的。SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。
SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的副本,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的二级缓存。由于Hibernate的二级缓存是作用在SessionFactory范围内的,因而它比一级缓存的范围更广,可以被所有的Session对象所共享。
二级缓存的适用范围
Hibernate的二级缓存作为一个可插入的组件在使用的时候也是可以进行配置的,但并不是所有的对象都适合放在二级缓存中。
在通常情况下会将具有以下特征的数据放入到二级缓存中:
● 很少被修改的数据。
● 不是很重要的数据,允许出现偶尔并发的数据。
● 不会被并发访问的数据。
● 参考数据。
在这里特别要注意的是对放入缓存中的数据不能有第三方的应用对数据进行更改(其中也包括在自己程序中使用其他方式进行数据的修改,例如,JDBC),因为那样Hibernate将不会知道数据已经被修改,也就无法保证缓存中的数据与数据库中数据的一致性。
二级缓存组件
在默认情况下,Hibernate会使用EHCache作为二级缓存组件。但是,可以通过设置hibernate.cache.provider_class属性,指定其他的缓存策略,该缓存策略必须实现org.hibernate.cache.CacheProvider接口。通过实现org.hibernate.cache.CacheProvider接口可以提供对不同二级缓存组件的支持。
Hibernate内置支持的二级缓存组件如表14.1所示。
组件 | Provider类 | 类型 | 集群 | 查询缓存 |
Hashtable | org.hibernate.cache.HashtableCacheProvider | 内存 | 不支持 | 支持 |
EHCache | org.hibernate.cache.EhCacheProvider | 内存,硬盘 | 不支持 | 支持 |
OSCache | org.hibernate.cache.OSCacheProvider | 内存,硬盘 | 不支持 | 支持 |
SwarmCache | org.hibernate.cache.SwarmCacheProvider | 集群 | 支持 | 不支持 |
JBoss TreeCache | org.hibernate.cache.TreeCacheProvider | 集群 | 支持 | 支持 |
下面我们就使用EhCache配置二级缓存为例,详细介绍一下二级缓存的配置方法 :
1)首先你要把ehcache-1.2.3.jar加入到当前应用的classpath中。
2)然后设置EhCache,建立配置文件ehcache.xml,默认的位置在class-path,可以放到你的src目录下,具体ehcache.xml文件内容如下:
- <?xmlversionxmlversion="1.0" encoding="UTF-8"?>
- <ehcache>
- <diskStorepathdiskStorepath="java.io.tmpdir"/>
- <defaultCache
- maxElementsInMemory="10000"<!-- 缓存最大数目 -->
- eternal="false"<!-- 缓存是否持久 -->
- overflowToDisk="true"<!-- 是否保存到磁盘,当系统当机时-->
- timeToIdleSeconds="300"<!-- 当缓存闲置n秒后销毁 -->
- timeToLiveSeconds="180"<!-- 当缓存存活n秒后销毁-->
- diskPersistent="false"
- diskExpiryThreadIntervalSeconds="120"/>
- <cache name="com.bzu.hibernate.Student"maxElementsInMemory="200"
- eternal="false"
- timeToIdleSeconds="50"
- timeToLiveSeconds="60"
- overflowToDisk="true"/>
- </ehcache>
3)配置完上边之后我们就要在Hibernate配置文件中设置,具体配置如下:
- <!-- 设置Hibernate的缓存接口类,这个类在Hibernate包中 -->
- <propertynamepropertyname="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
- <!-- 是否使用查询缓存 -->
- <propertynamepropertyname="hibernate.cache.use_query_cache">true</property>
如果使用spring调用Hibernate的sessionFactory的话,这样设置:
- <!--HibernateSession工厂管理 -->
- <beanidbeanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
- <propertynamepropertyname="hibernateProperties">
- <!--其他属性省略。。。-->
- <props>
- <propkeypropkey="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
- <propkeypropkey="hibernate.cache.use_query_cache">true</prop>
- <propkeypropkey="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
- </props>
- </property>
- <propertynamepropertyname="mappingDirectoryLocations">
- <list>
- <value>/WEB-INF/classes/cn/rmic/manager/hibernate/</value>
- </list>
- </property>
- </bean>
注意:如果不设置“查询缓存”,那么hibernate只会缓存使用load()方法获得的单个持久化对象,如果想缓存使用findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集的话,就需要设置hibernate.cache.use_query_cache true 才行
如果需要“查询缓存”,还需要在使用Query或Criteria()时设置其setCacheable(true);
配置完上边通用的配置之后,我们接下来要看怎么对特定的实体进行配置二级缓存了,这里的配置其实也很简单,就在对应的hbm文件中配置<cacheusage="read-write"/>就Ok了。我们以一个一对多的配置文件来看一下具体的hbm的配置:
多的一端:
- <hibernate-mapping>
- <classnameclassname="com.bzu.hibernate.Student" table="student">
- <!--注意:这一句需要紧跟在class标签下面,其他位置无效。 -->
- <cacheusagecacheusage="read-write" region="ehcache.xml中的name的属性值" />
- <id name="id" column="id"type="string">
- <generatorclassgeneratorclass="uuid"></generator>
- </id>
- <property name="name" column="name"type="string"></property>
- <property name="cardId" column="cardId"type="string"></property>
- <property name="age" column="age"type="int"></property>
- <many-to-one name="team"class="com.bzu.hibernate.Team"column="team_id"></many-to-one>
- </class>
- </hibernate-mapping>
一的一端配置:
- <set name="students" inverse="true"fetch="select" lazy="true"cascade="save-update">
- <!--注意:这一句需要紧跟在class标签下面,其他位置无效。 -->
- <cacheusagecacheusage="read-write" region="ehcache.xml中的name的属性值" />
- <keycolumnkeycolumn="team_id"></key>
- <one-to-manyclassone-to-manyclass="com.bzu.hibernate.Student"/>
- </set>
hbm文件查找cache方法名的策略:如果不指定hbm文件中的region="ehcache.xml中的name的属性值",则使用name名为com.ouou.model.Videos的cache,如果不存在与类名匹配的cache名称,则用defaultCache。
选择缓存策略依据:
<cache usage="transactional|read-write|nonstrict-read-write|read-only"/>
ehcache不支持transactional,其他三种可以支持。
read-only:无需修改, 那么就可以对其进行只读 缓存,注意,在此策略下,如果直接修改数据库,即使能够看到前台显示效果,
但是将对象修改至cache中会报error,cache不会发生作用。另:删除记录会报错,因为不能在read-only模式的对象从cache中删除。
read-write:需要更新数据,那么使用读/写缓存比较合适,前提:数据库不可以为serializable transaction isolation level
(序列化事务隔离级别)
nonstrict-read-write:只偶尔需要更新数据(也就是说两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离,
那么比较适合使用非严格读/写缓存策略。
注:在Spring托管的Hibernate中使用二级缓存
1.在spring的配置文件中,hibernate部分加入 xml 代码org.hibernate.cache.EhCacheProvider true
2.为HBM表设置cache策略 xml 代码
3.在DAO中,调用find方法查询之前,设置使用缓存 Java代码getHibernateTemplate().setCacheQueries(true);