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; }}
/* * 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(); }}
<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" parametercacheModel="CacheVideoInfo"> ....