hibernate:真正理解二级缓存和查询缓存

时间:2021-05-07 00:48:27

关于hibernate的O/R映射我已经写了多篇文章,并且受到了广泛的欢迎。hibernate不仅仅是社区上一个受欢迎的小孩,他实际上是非常强大一致和可信赖的数据库映射工具。映射Java中的对象到关系型数据库中有很多你需要知道的面。hibernate为了线程简单的启动做了特殊的处理,并为了超复杂的映射提供了便利。

数据库和你的Java对象间的映射一个主要的关注点是性能问题,那些没有花太多时间使用hibernate的人关心的问题是,这个O/R映射工具将会限制在性能上的提高。今天我将在二级缓存和查询缓存这两个方面来讨论他们关心的性能问题。

二级缓存

二级缓存被称作是二级的是因为在hibernate中在session打开的时候已经有一个缓存,从hibernate文档中我们可以看到:一个hibernate session是对于持久数据的事务级别的缓存,配置一个簇或者JVM级别(sessionfactory级别)的缓存是有可能的,缓存永远都不知道通过其他程序对持久化数据做的改变(可以配置过期时间)。正如上边提到的,二级缓存存在的时间和session factory存在时间一样长。二级缓存保存标记缓存的实体的属性和关联。 这里我不在详细介绍hibernate开发文档中的内容,所以对一些细节性的东西请参考它的开发文档。我只想说最重要的部分是添加在你的映射文件中的cache。
<cache usage="transactional|read-write|nonstrict-read-write|read-only" />

对于大多数读者,我到此为止描述的东西并没有什么新鲜的,相信我,马上就会讲到正题。

查询缓存

另一个有用的缓存是查询缓存,查询缓存有效的为特定的查询保存特定的实体,下边是文档中的描述:注意查询缓存并不在结果集中缓存实体的状态,它只缓存实体的值和实体的值得类型,所以查询缓存总是和二级缓存一起使用。关于它的配置可以在官方文档中查看。一旦在你的hibernate中配置生效,他就会简单的调用setCacheable(true)在你的查询或者事务对象上。

二级缓存是如何工作的

理解缓存的关键之一是理解他们在内部是怎么工作的,二级缓存尤其需要这样理解,特别是当处理大而复杂的对象而且需要频繁查询和加载的时候。第一件需要理解的事是二级缓存不缓存需要缓存的对象类型的实例,相反的它缓存的是对象属性的独立的值,对像下边这个对象:
public class Person {
private Person parent;
private Set<Person> children;

public void setParent(Person p) { parent = p; }
public void setChildren(Set<Person> set) { children = set; }

public Set<Person> getChildren() { return children; }
public Person getParent() { return parent; }
}
...with a mapping like this:

<class name="org.javalobby.tnt.hibernate.Person">

<cache usage="read-write"/>

<id name="id" column="id" type="long">
<generator class="identity"/>
</id>
<property name="firstName" type="string"/>
<property name="middleInitial" type="string"/>
<property name="lastName" type="string"/>
<many-to-one name="parent" column="parent_id" class="Person"/>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Person"/>
</set>
</class>

hibernate会为这个类按照以下的方式加载这个类的记录
*-----------------------------------------*
| Person Data Cache |
|-----------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 ] |
*-----------------------------------------*

所以在这个例子中,hibernate保存了3个字符串和为many-to-one关系的一个序列化的id,我重申一下hibernate不保存实际对象的实例。为什么这个非常重要呢,有两点,一:hibernate不需要担心客户端操作缓存中对象的代码;二:关系和关联不会变的‘陈旧’,很容易保持最新的数据。缓存不是树形的对象,是概念上的map数组,我使用‘概念上的’因为hibernate在缓存后提供了很多支持。

原文地址:http://www.javalobby.org/java/forums/t48846.html