最近看了查询缓存的2篇文章,特别有感悟,所以特别研究了下hibernate3源码,有了一点小小收获,所以这里总结一下
有2篇关于查询缓存的好文,这里推荐一下,感谢作者分享这么好的东西
数据库对象的缓存策略
[url]http://www.iteye.com/topic/9706[/url]
偶来谈谈Hibernte 的QueryCache。
[url]http://www.iteye.com/topic/10537[/url]
上面2篇文章都讲的是hibernate2的Query缓存策略,这里我详细讲解下Hibernate3的Query缓存策略,与《数据库对象的缓存策略》一文中提到的Hibernate2有所区别,与Jive的缓存策略相似,但针对ID查询不再去查询数据库,而是ID缓存中读取数据对象
[b]Hibernate3的Query缓存策略 [/b]
1.Hibernate的Query缓存策略的过程描述:
(1) 条件查询的请求一般都包括如下信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
(2) Hibernate首先根据这些信息组成一个Query Key,根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果ID列表根据Query Key放入到Query缓存中,再将每个ID对应的结果存放在配置的ID查询缓存中
(3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
2.Hibernate的Query缓存策略的优点
(1) 条件查询的时候,如果Query Key已经存在于缓存,那么不需要再查询数据库。命中的情况下,一次数据库查询也不需要。
3.Hibernate的Query缓存策略的缺点
(1) 条件查询涉及到的表中,如果有任何一条记录增加、删除、或改变,那么缓存中所有和该表相关的Query Key都会失效。
比如,有这样几组Query Key,它们的SQL里面都包括table1。
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 21, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c2 = ? …., parameter = ‘abc’, rowStart = 11, maxRows = 20.
当table1的任何数据对象(任何字段)改变、增加、删除的时候,这些Query Key对应的结果集都不能保证没有发生变化。
很难做到根据数据对象的改动精确判断哪些Query Key对应的结果集受到影响。最简单的实现方法,就是清空所有SQL包含table1的Query Key。
(2) Query缓存中,Query Key对应的是数据对象列表,假如不同的Query Key对应的数据对象列表有交集,那么,交集部分的数据对象不是重复存储的,只是ID重复存储,这与hibernate2完全不同,这也是hibernate3的改进之处
比如,Query Key 1对应的数据对象列表为{a(id = 1), b(id = 2)},Query Key 2对应的数据对象列表为{a(id = 1), c(id = 3)},这个a的ID存了2份,但是ID对应的数据对象只存了一份
[color=brown][b]下面我讲下如何使用[/b][/color]
我这里使用的是二级缓存ehcahce,配置如下:
ehcache.xml
ID查询缓存还需要在Business.hbm.xml文件中加入缓存配置,如下
最后是在Query执行代码中要加上setCacheable(true)设置应用缓存策略
MmsSendStatDAOForMYSQL4.java
执行以上程序,打开log4j日志为debug级别查看缓存是否应用
log4j.logger.org.hibernate.cache.StandardQueryCache = debug
日志输出如下:
[quote]DEBUG [http-8080-Processor22] (StandardQueryCache.java:104) - checking cached query results in region: org.hibernate.cache.StandardQueryCache
DEBUG [http-8080-Processor22] (StandardQueryCache.java:162) - Checking query spaces for up-to-dateness: [tuke_mmsmsys.business]
DEBUG [http-8080-Processor22] (StandardQueryCache.java:119) - returning cached query results[/quote]
有2篇关于查询缓存的好文,这里推荐一下,感谢作者分享这么好的东西
数据库对象的缓存策略
[url]http://www.iteye.com/topic/9706[/url]
偶来谈谈Hibernte 的QueryCache。
[url]http://www.iteye.com/topic/10537[/url]
上面2篇文章都讲的是hibernate2的Query缓存策略,这里我详细讲解下Hibernate3的Query缓存策略,与《数据库对象的缓存策略》一文中提到的Hibernate2有所区别,与Jive的缓存策略相似,但针对ID查询不再去查询数据库,而是ID缓存中读取数据对象
[b]Hibernate3的Query缓存策略 [/b]
1.Hibernate的Query缓存策略的过程描述:
(1) 条件查询的请求一般都包括如下信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。
(2) Hibernate首先根据这些信息组成一个Query Key,根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果ID列表根据Query Key放入到Query缓存中,再将每个ID对应的结果存放在配置的ID查询缓存中
(3) Query Key中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。
2.Hibernate的Query缓存策略的优点
(1) 条件查询的时候,如果Query Key已经存在于缓存,那么不需要再查询数据库。命中的情况下,一次数据库查询也不需要。
3.Hibernate的Query缓存策略的缺点
(1) 条件查询涉及到的表中,如果有任何一条记录增加、删除、或改变,那么缓存中所有和该表相关的Query Key都会失效。
比如,有这样几组Query Key,它们的SQL里面都包括table1。
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? …., parameter = 1, rowStart = 21, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c1 = ? ….., parameter = 2, rowStart = 11, maxRows = 20.
SQL = select * from table1 where c2 = ? …., parameter = ‘abc’, rowStart = 11, maxRows = 20.
当table1的任何数据对象(任何字段)改变、增加、删除的时候,这些Query Key对应的结果集都不能保证没有发生变化。
很难做到根据数据对象的改动精确判断哪些Query Key对应的结果集受到影响。最简单的实现方法,就是清空所有SQL包含table1的Query Key。
(2) Query缓存中,Query Key对应的是数据对象列表,假如不同的Query Key对应的数据对象列表有交集,那么,交集部分的数据对象不是重复存储的,只是ID重复存储,这与hibernate2完全不同,这也是hibernate3的改进之处
比如,Query Key 1对应的数据对象列表为{a(id = 1), b(id = 2)},Query Key 2对应的数据对象列表为{a(id = 1), c(id = 3)},这个a的ID存了2份,但是ID对应的数据对象只存了一份
[color=brown][b]下面我讲下如何使用[/b][/color]
我这里使用的是二级缓存ehcahce,配置如下:
ehcache.xml
<!-- hibernate默认的查询缓存配置 -->
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="10000"
timeToLiveSeconds="10000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- ID查询缓存配置,其实就是针对com.tukechina.mms.pojos.Business对象的缓存策略 -->
<cache name="com.tukechina.mms.pojos.Business"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="10000"
timeToLiveSeconds="10000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
ID查询缓存还需要在Business.hbm.xml文件中加入缓存配置,如下
<hibernate-mapping>
<class name="com.tukechina.mms.pojos.Business" table="business" catalog="tuke_mmsmsys">
<!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->
<cache usage="read-write"/>
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="nameCh" type="string">
<column name="name_ch" length="50" not-null="true" />
</property>
<property name="nameEn" type="string">
<column name="name_en" length="50" not-null="true" />
</property>
<property name="level" type="int">
<column name="level" not-null="true" />
</property>
<property name="productId" type="int">
<column name="product_id" not-null="true" />
</property>
<property name="cp" type="int">
<column name="cp" not-null="true" />
</property>
<property name="description" type="string">
<column name="description" length="65535" />
</property>
<property name="code" type="int">
<column name="code" not-null="true" />
</property>
<property name="insertTime" type="timestamp">
<column name="insert_time" length="0" />
</property>
<property name="status" type="int">
<column name="status" not-null="true" />
</property>
<property name="endTime" type="timestamp">
<column name="end_time" length="0" />
</property>
<property name="cooperateWay" type="string">
<column name="cooperate_way" length="50" />
</property>
<property name="incomeDivideWay" type="string">
<column name="income_divide_way" length="50" />
</property>
<property name="area" type="string">
<column name="area" length="50" />
</property>
<property name="areacode" type="string">
<column name="areacode" length="10" />
</property>
<property name="updateTime" type="timestamp">
<column name="update_time" length="0" />
</property>
<property name="sendType" type="int">
<column name="send_type" not-null="true" />
</property>
<property name="sendId" type="java.lang.Integer">
<column name="send_id" />
</property>
</class>
</hibernate-mapping>
最后是在Query执行代码中要加上setCacheable(true)设置应用缓存策略
MmsSendStatDAOForMYSQL4.java
String bqueryString = "select a from "
+ Business.class.getName() + " a";
if (admin.getType() != 0) {// 判断管理员权限
bqueryString += ","
+ RelevantAuthority.class.getName()
+ " b where a.status!=2 and a.id = b.relatedId and b.type=4 and b.account='"
+ admin.getAccount() + "'";
} else {
bqueryString += " where a.status!=2 ";
}
Query bquery = s.createQuery(bqueryString);
//设置缓存应用
bquery.setCacheable(true);
//bquery.setCacheMode(CacheMode.GET);
List<Business> blist = bquery.list();
执行以上程序,打开log4j日志为debug级别查看缓存是否应用
log4j.logger.org.hibernate.cache.StandardQueryCache = debug
日志输出如下:
[quote]DEBUG [http-8080-Processor22] (StandardQueryCache.java:104) - checking cached query results in region: org.hibernate.cache.StandardQueryCache
DEBUG [http-8080-Processor22] (StandardQueryCache.java:162) - Checking query spaces for up-to-dateness: [tuke_mmsmsys.business]
DEBUG [http-8080-Processor22] (StandardQueryCache.java:119) - returning cached query results[/quote]