• memcached整合ibatis


    ibatis自带的本地缓存有FIFO,LRU等,对于分布式缓存也有osCache支持,而最常用的memcached也可以整合到ibatis里滴,这样通过map关系配置,就省了很多硬编码。
    首先写个实现CacheController接口的MemcachedIbatisController类
    /**
     * ibatis管理memcache 使用LRU算法
     * @author langke93
     * @date 2011-01-17
     * @usage:
     *  <cacheModel id="cache-videoinfo" type="com.woyo.upload.kernel.util.MemcachedIbatisController">
          <flushInterval seconds="3600"/>
           <flushOnExecute statement="updateVideoInfo" />
         <flushOnExecute statement="insertVideoInfo" />
         <flushOnExecute statement="deleteVideoInfo" />
          <property name="size" value="1000" />
        </cacheModel>
        @modify:
         cacheSize默认为0 ,表示由memcache管理缓存大小,推荐在集群中使用
     */
    package com.woyo.upload.kernel.util;
    import java.util.Collections;
    import java.util.Date;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Properties;
    import org.apache.log4j.Logger;
    import com.ibatis.sqlmap.engine.cache.CacheController;
    import com.ibatis.sqlmap.engine.cache.CacheModel;
    public class MemcachedIbatisController implements CacheController {
        protected final Logger log = Logger.getLogger(this.getClass());
        private MemCachedManager cache;    
        private List<String> keyList;//keyList 管理key
        private int cacheSize;   
      
        public MemcachedIbatisController(){
            this.cacheSize = 0; //默认为0表示不限制缓存大小,不使用LRU
            this.cache = MemCachedManager.getInstance();
            this.keyList = getKeyListInstance();
            log.info("       Enable MemcachedIbatisController !");
        }   
        public synchronized List<String> getKeyListInstance(){
         if(keyList == null){
          keyList =  Collections.synchronizedList(new LinkedList<String>());
         }
         return keyList;
        }
        public void flush(CacheModel cacheModel) {
         String key = null;   
            try {
             if(keyList.isEmpty()){
              //cache.flush(); 目前清除缓存仅依赖于缓存过期,分表/条件清除缓存还需要做扩展
             }else
                for (int i=0;i<keyList.size();i++) {
                 key = keyList.get(i);
              cache.delete(key);
              keyList.remove(key); 
                }
            } catch (Exception e) {   
                e.printStackTrace();   
            }
            //keyList.clear();
            log.info("       flush memcache!"); 
        }
        /**
         * 实现LRU算法
         * 把最近取过的数据放到栈顶
         */
        public Object getObject(CacheModel cacheModel, Object key) {
         Object result ;
            String ckey = getKey(cacheModel, key);
            try {
                result = cache.get(ckey);
                if(cacheSize>0){//如果缓存大小有限制,则使用LRU算法
                    keyList.remove(ckey);
                    if (result != null) {
                      keyList.add(ckey);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();   
                return null;   
            }
            log.info("       getObject memcache!!");
            return result ;
        }   
      
        /**
         * 如果超过缓存大小限制,移出栈底数据
         */
        public void putObject(CacheModel cacheModel, Object key, Object object) {   
            String ckey = getKey(cacheModel, key);   
            keyList.add(ckey);
            try {
             Date expiry = new Date();//过期时间
             expiry.setTime(expiry.getTime()+cacheModel.getFlushInterval());
                cache.add(ckey,object,expiry);
                log.debug("       putObject memcache!"); 
                log.info("cacheSize:"+cacheSize+",keyListSize:"+keyList.size());
                if (cacheSize>0 && keyList.size() > cacheSize) {   
                    String oldestKey = keyList.remove(0);   
                    cache.delete(oldestKey);   
                    log.debug("       keyList.oldestKey memcache!"); 
                }   
            } catch (Exception e) {   
                e.printStackTrace();   
            }   
      
        }   
      
        public Object removeObject(CacheModel cacheModel, Object key) {
            log.info("       removeObject memcache!"); 
            String ckey = getKey(cacheModel, key);   
            try {   
                if (keyList.contains(ckey)) {
                 keyList.remove(ckey);
                    log.info("       removeObject memcache!"); 
                    return cache.delete(ckey);   
                }   
            } catch (Exception e) {   
                e.printStackTrace();   
            }   
            return null;   
        }   
        public void setProperties(Properties props){ 
            String size = props.getProperty("size");   
            if (size == null) {
                size = props.getProperty("cache-size");   
            }   
            if (size != null) {
                cacheSize = Integer.parseInt(size);   
            }
            if(cache!=null)
             cache =  MemCachedManager.getInstance();
        }
        
        public int getCacheSize() {
            return cacheSize;
          }
          public void setCacheSize(int cacheSize) {
            this.cacheSize = cacheSize;
          }
          
        private String getKey(CacheModel cacheModel, Object cacheKey) {   
            String key = cacheKey.toString();   
            int keyhash = key.hashCode();   
            String cacheId = cacheModel.getId();
            key = Config.MEMCACHED_PRE + cacheId + "_" + keyhash; 
            return key;
        }
    }
    对于分布式/集群环境下,每个实例的key会不一至,也就是说A机器存入的一条SQL的KEY在B机器里算出来的KEY会不一至。原因是ibatis在key的算法里,把statement id的hash码做为key的一部分,所以需要重写CachingStatement类,修改baseCacheKey(下文高亮色的地方):
    /*
     *  Copyright 2004 Clinton Begin
     *
     *  Licensed under the Apache License, Version 2.0 (the "License");
     *  you may not use this file except in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing, software
     *  distributed under the License is distributed on an "AS IS" BASIS,
     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *  See the License for the specific language governing permissions and
     *  limitations under the License.
     */
    package com.ibatis.sqlmap.engine.mapping.statement;
    import com.ibatis.sqlmap.client.event.RowHandler;
    import com.ibatis.sqlmap.engine.cache.CacheKey;
    import com.ibatis.sqlmap.engine.cache.CacheModel;
    import com.ibatis.sqlmap.engine.mapping.parameter.ParameterMap;
    import com.ibatis.sqlmap.engine.mapping.result.ResultMap;
    import com.ibatis.sqlmap.engine.mapping.sql.Sql;
    import com.ibatis.sqlmap.engine.scope.StatementScope;
    import com.ibatis.sqlmap.engine.transaction.Transaction;
    import java.sql.SQLException;
    import java.util.List;
    /**
     * 
     * @modify langnke93
     * statement.setBaseCacheKey(0);
     * baseCacheKey使用固定值,保证每个实例的CacheKey一至
     *
     */
    public class CachingStatement extends MappedStatement {
      private MappedStatement statement;
      private CacheModel cacheModel;
      public CachingStatement(MappedStatement statement, CacheModel cacheModel) {
        this.statement = statement;
        this.cacheModel = cacheModel;
      }
      public String getId() {
        return statement.getId();
      }
      public StatementType getStatementType() {
        return statement.getStatementType();
      }
      public Integer getResultSetType() {
        return statement.getResultSetType();
      }
      public Integer getFetchSize() {
        return statement.getFetchSize();
      }
      public ParameterMap getParameterMap() {
        return statement.getParameterMap();
      }
      public ResultMap getResultMap() {
        return statement.getResultMap();
      }
      public int executeUpdate(StatementScope statementScope, Transaction trans, Object parameterObject)
          throws SQLException {
        int n = statement.executeUpdate(statementScope, trans, parameterObject);
        return n;
      }
      public Object executeQueryForObject(StatementScope statementScope, Transaction trans, Object parameterObject, Object resultObject)
          throws SQLException {
        CacheKey cacheKey = getCacheKey(statementScope, parameterObject);
        cacheKey.update("executeQueryForObject");
        Object object = cacheModel.getObject(cacheKey);
        if (object == CacheModel.NULL_OBJECT){
         // This was cached, but null
         object = null;
        }else if (object == null) {
           object = statement.executeQueryForObject(statementScope, trans, parameterObject, resultObject);
           cacheModel.putObject(cacheKey, object);
        }
        return object;
      }
      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;
      }
      public void executeQueryWithRowHandler(StatementScope statementScope, Transaction trans, Object parameterObject, RowHandler rowHandler)
          throws SQLException {
        statement.executeQueryWithRowHandler(statementScope, trans, parameterObject, rowHandler);
      }
      public CacheKey getCacheKey(StatementScope statementScope, Object parameterObject) {
     statement.setBaseCacheKey(0);//去掉取于statement id 动态的baseCacheKey使用固定值,保证每个实例的CacheKey一至,用于集群环境
        CacheKey key = statement.getCacheKey(statementScope, parameterObject);
        if (!cacheModel.isReadOnly() && !cacheModel.isSerialize()) {
          key.update(statementScope.getSession());
        }
        return key;
      }
      public void setBaseCacheKey(int base) {
        statement.setBaseCacheKey(base);
      }
      public void addExecuteListener(ExecuteListener listener) {
        statement.addExecuteListener(listener);
      }
      public void notifyListeners() {
        statement.notifyListeners();
      }
      public void initRequest(StatementScope statementScope) {
        statement.initRequest(statementScope);
      }
      public Sql getSql() {
        return statement.getSql();
      }
      public Class getParameterClass() {
        return statement.getParameterClass();
      }
      public Integer getTimeout() {
        return statement.getTimeout();
      }
      public boolean hasMultipleResultMaps() {
        return statement.hasMultipleResultMaps();
      }
      public ResultMap[] getAdditionalResultMaps() {
        return statement.getAdditionalResultMaps();
      }
    }
    好了,这样就可以在map配置里引用刚才的cache实现类
     <cacheModel id="CacheVideoInfo"  readOnly="false" serialize="true" type="com.woyo.upload.kernel.util.MemcachedIbatisController">
          <flushInterval seconds="3600"/>
          <property name="size" value="1000" />
        </cacheModel>
     <statement id="updateVideoInfo" parameterClass="com.woyo.upload.kernel.entity.VideoInfo" cacheModel="CacheVideoInfo">  ....
    目前清除缓存仅依赖于缓存过期,分实例/条件清除缓存还需要做扩展
     
  • 相关阅读:
    JAVA应用程序占用CPU、内存过高分析过程
    html和css问题?
    html跳动的心实现代码
    css知识点总结
    html注册栏网页练习代码
    前端开发单词大全
    html常用代码合集
    html背景图星际导航图练习
    html迪士尼网页实现代码
    html阿里云网页练习实现代码
  • 原文地址:https://www.cnblogs.com/langke93/p/2217387.html
Copyright © 2020-2023  润新知