某些特定操作不需要初始化代理
equals(), 假如持久化类没有重载equals()
hashCode(), 假如持久化类没有重载hashCode()
标识符的get方法
Hibernate会识别出重载了equals() 或者 hashCode()方法的持久化类。
在初始化代理的时候发生的异常会被包装成LazyInitializationException。
有时候我们需要保证在Session关闭前某个代理或者集合已经被初始化了。当然,我们总是可以通过调用cat.getSex()或者 cat.getKittens().size()之类的方法来确保这一点。但是这样程序可读性不佳,也不符合通常的代码规范。静态方法Hibernate.initialize()和Hibernate.isInitialized()给你的应用程序一个正常的途径来加载集合或代理。Hibernate.initialize(cat) 会强制初始化一个代理,cat,只要它的Session仍然打开。Hibernate.initialize( cat.getKittens() )对kittens的集合具有同样的功能。
14.3. 第二层缓存(The Second Level Cache)s
HibernateSession是事务级别的持久化数据缓存。再为每个类或者每个集合配置一个集群或者JVM级别(SessionFactory级别)的缓存也是有可能的。你甚至可以插入一个集群的缓存。要小心,缓存永远不会知道其他进程可能对持久化仓库(数据库)进行的修改(即使他们可能设定为经常对缓存的数据进行失效)。
默认情况下,Hibernate使用EHCache进行JVM级别的缓存。但是,对JCS的支持现在已经被废弃了,未来版本的Hibernate将会去掉它。通过hibernate.cache.provider_class属性,你也可以指定其他缓存,只要其实现了net.sf.hibernate.cache.CacheProvider接口。
表 14.1. Cache Providers
Cache Provider class Type Cluster Safe Query Cache Supported
Hashtable (not intended for production use) net.sf.hibernate.cache.HashtableCacheProvider memory yes
EHCache net.sf.hibernate.cache.EhCacheProvider memory, disk yes
OSCache net.sf.hibernate.cache.OSCacheProvider memory, disk yes
SwarmCache net.sf.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)
JBoss TreeCache net.sf.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication)
14.3.1. 对映射(Mapping)缓冲
类或者集合映射的<cache>元素可能有下列形式:
<cache (1)
usage="transactional|read-write|nonstrict-read-write|read-only" />
(1) usage 指定了缓存策略: transactional, read-write, nonstrict-read-write 或者 read-only
另外 (推荐首选?), 你可以在hibernate.cfg.xml中指定<class-cache> 和 <collection-cache> 元素。
usage属性指明了缓存并发策略(cache concurrency strategy)。
14.3.2. 策略:只读缓存
如果你的应用程序需要读取一个持久化类的实例,但是并不打算修改它们,可以使用read-only 缓存。这是最简单,也是实用性最好的策略。甚至在集群中,它也能完美地运作。
<class name="eg.Immutable" mutable="false">
<cache usage="read-only"/>
....
</class>
14.3.3. 策略:读/写缓存
如果应用程序需要更新数据,可能read-write缓存比较合适。如果需要可序列化事务隔离级别(serializable transaction isolation level),这种缓存决不能使用。如果在JTA环境中使用这种缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值,给出得到JTA TransactionManager的策略。在其它环境中,你必须确保在Session.close()或者Session.disconnect()调用前,事务已经结束了。 如果你要在集群环境下使用这一策略,你必须确保底层的缓存实现支持锁定(locking)。内置的缓存提供器并不支持。
<class name="eg.Cat" .... >
<cache usage="read-write"/>
....
<set name="kittens" ... >
<cache usage="read-write"/>
....
</set>
</class>
14.3.4. 策略:不严格的读/写缓存
如果程序偶尔需要更新数据(也就是说,出现两个事务同时更新同一个条目的现象很不常见),也不需要十分严格的事务隔离,可能适用nonstrict-read-write缓存。如果在JTA环境中使用这种缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值,给出得到JTA TransactionManager的策略。在其它环境中,你必须确保在Session.close()或者Session.disconnect()调用前,事务已经结束了。
14.3.5. 策略:事务缓存(transactional)
transactional缓存策略提供了对全事务缓存提供,比如JBoss TreeCache的支持。这样的缓存只能用于JTA环境,你必须指定hibernate.transaction.manager_lookup_class。
没有一种缓存提供器能够支持所有的缓存并发策略。下面的表列出每种提供器与各种并发策略的兼容性。
表 14.2. 缓存并发策略支持(Cache Concurrency Strategy Support)
Cache read-only nonstrict-read-write read-write transactional
Hashtable (not intended for production use) yes yes yes
EHCache yes yes yes
OSCache yes yes yes
SwarmCache yes yes
JBoss TreeCache yes yes
14.4. 管理Session缓存
不管何时你传递一个对象给save(), update()或者 saveOrUpdate() ,或者不管何时你使用load(), find(), iterate()或者filter()取得一个对象的时候,该对象被加入到Session的内部缓存中。当后继的flush()被调用时,对象的状态会和数据库进行同步。如果你在处理大量对象并且需要有效的管理内存的时候,你可能不希望发生这种同步,evict()方法可以从缓存中去掉对象和它的集合。
Iterator cats = sess.iterate("from eg.Cat as cat"); //a huge result set
while ( cats.hasNext() ) {
Cat cat = (Cat) iter.next();
doSomethingWithACat(cat);
sess.evict(cat);
}
Hibernate will evict associated entities automatically if the association is mapped with cascade="all" or cascade="all-delete-orphan". 如果关联通过cascade="all" 或者 cascade="all-delete-orphan"实现,Hibernate会自动删除关联的实体。
Session也提供了一个contains()方法来判断是否一个实例处于这个session的缓存中。
要把所有的对象从session缓存中完全清除,请调用Session.clear()。
对于第二层缓存来说,在SessionFactory中定义了一些方法来从缓存中清除一个实例、整个类、集合实例或者整个集合。
14.5. 查询缓存(Query Cache)
查询结果集也可以被缓存。只有当经常使用同样的参数进行查询时,这才会有些用处。要使用查询缓存,首先你要打开它,设置hibernate.cache.use_query_cache=true这个属性。这样会创建两个缓存区域——一个保存查询结果集(net.sf.hibernate.cache.QueryCache),另一个保存最近查询的表的时间戳(net.sf.hibernate.cache.UpdateTimestampsCache)。请注意查询缓存并不缓存结果集中包含实体的状态;它只缓存标识符属性的值和值类型的结果。所以查询缓存通常会和第二层缓存一起使用。
大多数查询并不会从缓存中获得什么好处,所以默认查询是不进行缓存的。要进行缓存,调用 Query.setCacheable(true)。这个调用会让查询在执行时去从缓存中查找结果,或者把结果集放到缓存去。
如果你要对查询缓存的失效政策进行精确的控制,你必须调用Query.setCacheRegion()来为每个查询指定一个命名的缓存区域。
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
.setEntity("blogger", blogger)
.setMaxResults(15)
.setCacheable(true)
.setCacheRegion("frontpages")
.list();