`
lifei114
  • 浏览: 26100 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

IBatis 的缓存机制之 Select Query缓存

阅读更多

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>

 

我们来跟踪一下代码,看 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的参数变化的频率太多时,使用缓存的效果将十分不明显,

甚至可能取得相反的效果,每次查询缓存也是需要时间的。至于 IBatis 何时更新缓存里的数据,请参看我之前写的这遍文章 IBatis 的缓存机制( http://lifei114.iteye.com/admin/blogs/581795

具体项目中如何使用缓存我认为具体问题具体分析,不要盲目相信科学神话,“大家都在用的东西不一定就适合自己用”,以上供大家参考,欢迎大家一起讨论,我的 QQ: 535043378 ,最近加的人太多请注明 "JavaEye"。

r

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics