原文地址:http://lifei114.javaeye.com/admin/blogs/581795
IBatis 的缓存机制之 Select Query缓存
可以按下面的代码在你的 SqlMap.xml 里配置,如下:
<
cacheModel
id
="users-cache"
imlementation
="LRU"
readOnly
="true"
serialize
="true"
>
< flushInterval hours ="24" />
< flushOnExecute statement ="users.update" />
< property name ="size" value ="1000" />
</ cacheModel >
< statement id ="findUsers" parameterClass ="int" cacheModel ="users-cache" >
select * from users where member_id = #value#
</ statement >
< flushInterval hours ="24" />
< flushOnExecute statement ="users.update" />
< property name ="size" value ="1000" />
</ cacheModel >
< statement id ="findUsers" parameterClass ="int" cacheModel ="users-cache" >
select * from users where member_id = #value#
</ statement >
我们来跟踪一下代码,看 IBatis 是如何实现缓存的,
还是由类:SqlMapParser 来解析这个配置的,代码如下:
- private void addCacheModelNodelets() {
- parser.addNodelet("/sqlMap/cacheModel", new Nodelet() {
- public void process(Node node) throws Exception {
- Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- String id = state.applyNamespace(attributes.getProperty("id"));
- String type = attributes.getProperty("type");
- String readOnlyAttr = attributes.getProperty("readOnly");
- Boolean readOnly = readOnlyAttr == null || readOnlyAttr.length() <= 0 ? null : new Boolean("true".equals(readOnlyAttr));
- String serializeAttr = attributes.getProperty("serialize");
- Boolean serialize = serializeAttr == null || serializeAttr.length() <= 0 ? null : new Boolean("true".equals(serializeAttr));
- type = state.getConfig().getTypeHandlerFactory().resolveAlias(type);
- Class clazz = Resources.classForName(type);
- if (readOnly == null) {
- readOnly = Boolean.TRUE;
- }
- if (serialize == null) {
- serialize = Boolean.FALSE;
- }
- CacheModelConfig cacheConfig = state.getConfig().newCacheModelConfig(id, (CacheController) Resources.instantiate(clazz), readOnly.booleanValue(), serialize.booleanValue());
- state.setCacheConfig(cacheConfig);
- }
- });
- parser.addNodelet("/sqlMap/cacheModel/end()", new Nodelet() {
- public void process(Node node) throws Exception {
- state.getCacheConfig().setControllerProperties(state.getCacheProps());
- }
- });
- parser.addNodelet("/sqlMap/cacheModel/property", new Nodelet() {
- public void process(Node node) throws Exception {
- state.getConfig().getErrorContext().setMoreInfo("Check the cache model properties.");
- Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- String name = attributes.getProperty("name");
- String value = NodeletUtils.parsePropertyTokens(attributes.getProperty("value"), state.getGlobalProps());
- state.getCacheProps().setProperty(name, value);
- }
- });
- parser.addNodelet("/sqlMap/cacheModel/flushOnExecute", new Nodelet() {
- public void process(Node node) throws Exception {
- Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- String statement = childAttributes.getProperty("statement");
- state.getCacheConfig().addFlushTriggerStatement(statement);
- }
- });
- parser.addNodelet("/sqlMap/cacheModel/flushInterval", new Nodelet() {
- public void process(Node node) throws Exception {
- Properties childAttributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- try {
- int milliseconds = childAttributes.getProperty("milliseconds") == null ? 0 : Integer.parseInt(childAttributes.getProperty("milliseconds"));
- int seconds = childAttributes.getProperty("seconds") == null ? 0 : Integer.parseInt(childAttributes.getProperty("seconds"));
- int minutes = childAttributes.getProperty("minutes") == null ? 0 : Integer.parseInt(childAttributes.getProperty("minutes"));
- int hours = childAttributes.getProperty("hours") == null ? 0 : Integer.parseInt(childAttributes.getProperty("hours"));
- state.getCacheConfig().setFlushInterval(hours, minutes, seconds, milliseconds);
- } catch (NumberFormatException e) {
- throw new RuntimeException("Error building cache in '" + "resourceNAME" + "'. Flush interval milliseconds must be a valid long integer value. Cause: " + e, e);
- }
- }
- });
- }
带缓存的SQL具体的执行是由类:CachingStatement 来执行,如要执行executeQueryForList动作,其执行的代码如下:
- public List executeQueryForList(StatementScope statementScope, Transaction trans, Object parameterObject, int skipResults, int maxResults)
- throws SQLException {
- CacheKey cacheKey = getCacheKey(statementScope, parameterObject);
- cacheKey.update("executeQueryForList");
- cacheKey.update(skipResults);
- cacheKey.update(maxResults);
- Object listAsObject = cacheModel.getObject(cacheKey);
- List list;
- if(listAsObject == CacheModel.NULL_OBJECT){
- // The cached object was null
- list = null;
- }else if (listAsObject == null) {
- list = statement.executeQueryForList(statementScope, trans, parameterObject, skipResults, maxResults);
- cacheModel.putObject(cacheKey, list);
- }else{
- list = (List) listAsObject;
- }
- return list;
- }
中去,再次使用时就直接返回缓存里的数据了。
这里面的 CacheKey 很有意思,Ibatis 是按照 SQL 加参数并加参数个数进行 Hash 组成一个 Key。因此我们的查询中 SQL的参数变化的频率太多时,使用缓存的效果将十分不明显,
甚至可能取得相反的效果,每次查询缓存也是需要时间的。
具体项目中如何使用缓存我认为具体问题具体分析,不要盲目相信科学神话,“大家都在用的东西不一定就适合自己用”
public List executeQueryForList(StatementScope statementScope, Transaction trans, Object parameterObject, int skipResults, int maxResults)
throws SQLException {
CacheKey cacheKey = getCacheKey(statementScope, parameterObject);
cacheKey.update("executeQueryForList");
cacheKey.update(skipResults);
cacheKey.update(maxResults);
Object listAsObject = cacheModel.getObject(cacheKey);
List list;
if(listAsObject == CacheModel.NULL_OBJECT){
// The cached object was null
list = null;
}else if (listAsObject == null) {
list = statement.executeQueryForList(statementScope, trans, parameterObject, skipResults, maxResults);
cacheModel.putObject(cacheKey, list);
}else{
list = (List) listAsObject;
}
return list;
}