1、下载xmemcached-1.3.8.jar
2、实现CacheController接口
MemcachedIbatisController.java
package com.xxxxxx.memcached; import java.text.MessageFormat; import java.util.Collections; 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.CacheKey; import com.ibatis.sqlmap.engine.cache.CacheModel; public class MemcachedIbatisController implements CacheController { private static Logger LOG = Logger.getLogger(MemcachedIbatisController.class); private MemcachedManager cache; private List<String> keyList; private int cacheSize; private boolean initExpiry = false; private int expiry = 0; public MemcachedIbatisController() { if (LOG.isDebugEnabled()) { LOG.info("Enable MemcachedIbatisController !"); } this.cacheSize = 0; this.cache = MemcachedProxy.memcachedManager; this.keyList = getKeyListInstance(); } public synchronized List<String> getKeyListInstance() { if (keyList == null) { keyList = Collections.synchronizedList(new LinkedList<String>()); } return keyList; } public void flush(CacheModel cacheModel) { if (LOG.isInfoEnabled()) { LOG.info("Flush memcache!"); } String key = null; try { if (keyList.isEmpty()) { cache.flushAll(); } else for (int i = 0; i < keyList.size(); i++) { key = keyList.get(i); cache.delete(key); } } catch (Exception e) { LOG.error("MemcachedIbatisController method flush {}", e); } finally { if (keyList.size() > 0) { keyList.clear(); } } } public Object getObject(CacheModel cacheModel, Object key) { Object result; String ckey = getKey(cacheModel, key); LOG.error(key); try { result = cache.get(ckey); if (cacheSize > 0) { keyList.remove(ckey); if (result != null) { keyList.add(ckey); } } } catch (Exception e) { LOG.error("MemcachedIbatisController method getObject {}", e); return null; } if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format("Get the {0} from memcached, value={1}", ckey, result)); } return result; } public void putObject(CacheModel cacheModel, Object key, Object object) { String ckey = getKey(cacheModel, key); keyList.add(ckey); try { if (!initExpiry) { expiry = new Long(cacheModel.getFlushInterval()).intValue() / 1000; initExpiry = true; } boolean ret = cache.set(ckey, expiry, object); if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format("Add {0} to memcached, returnt state={1}", ckey, ret)); LOG.info("CacheSize:" + cacheSize + ", keyListSize:" + keyList.size()); } if (cacheSize > 0 && keyList.size() > cacheSize) { String oldestKey = keyList.remove(0); ret = cache.delete(oldestKey); if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format("Remove {0} to memcached, returnt state={1}", ckey, ret)); } } } catch (Exception e) { LOG.error("MemcachedIbatisController method putObject {}", e); } } public Object removeObject(CacheModel cacheModel, Object key) { String ckey = getKey(cacheModel, key); try { if (keyList.contains(ckey)) { keyList.remove(ckey); if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format("Remove {0} from keyList!", ckey)); } boolean ret = cache.delete(ckey); if (LOG.isInfoEnabled()) { LOG.info(MessageFormat.format("Delete {0} from memcached, returnt state={1}", ckey, ret)); } return ret; } } catch (Exception e) { LOG.error("MemcachedIbatisController method removeObject {}", e); } return null; } public void setProperties(Properties props) { if (LOG.isDebugEnabled()) { LOG.debug("Set Properties"); } String size = props.getProperty("cache-size"); if (size != null) { cacheSize = Integer.parseInt(size); } } public int getCacheSize() { return cacheSize; } public void setCacheSize(int cacheSize) { this.cacheSize = cacheSize; } private String getKey(CacheModel cacheModel, Object cacheKey) { //CacheKey ck = (CacheKey)cacheKey; String key = cacheKey.toString(); int keyhash = key.hashCode(); String cacheId = cacheModel.getId(); key = "IBATIS_CACHED" + "_" + cacheId + "_" + keyhash; return key; } @Override public void configure(Properties arg0) { // do what? sorry i don`t know } }
3、memcache的操作类和代理
MemcachedManager.java
package com.xxxxxx.memcached; import net.rubyeye.xmemcached.MemcachedClient; import org.apache.log4j.Logger; public class MemcachedManager { private static Logger LOG = Logger.getLogger(MemcachedManager.class); private static MemcachedClient memcachedClient; public MemcachedManager() { super(); } public boolean set(String key, int expiry, Object o) { try { return memcachedClient.set(key, expiry, o); } catch (Exception e) { LOG.error("MemcachedManager method set {}", e); } return false; } public Object get(String key) { Object result = null; try { result = memcachedClient.get(key); } catch (Exception e) { LOG.error("MemcachedManager method get {}", e); } return result; } public boolean delete(String key) { try { return memcachedClient.delete(key); } catch (Exception e) { LOG.error("MemcachedManager method delete {}", e); } return false; } public void flushAll() { try { memcachedClient.flushAll(); } catch (Exception e) { LOG.error("MemcachedManager method flushAll {}", e); } } public void setMemcachedClient(MemcachedClient memcachedClient) { if (null == MemcachedManager.memcachedClient) MemcachedManager.memcachedClient = memcachedClient; } }
MemcachedProxy.java
package com.xxxxxx.memcached; public class MemcachedProxy { public static MemcachedManager memcachedManager; public MemcachedManager getMemcachedManager() { return memcachedManager; } public void setMemcachedManager(MemcachedManager memcachedManager) { if (null == MemcachedProxy.memcachedManager) MemcachedProxy.memcachedManager = memcachedManager; } }
4、配置spring的配置文件
该配置一定要写在ibatis配置之前,保证,先初始化(如果使用扫描器注入bean,要写在扫描器之前)
<!-- 注意:memcached 初始化要在ibatis初始化之前,否则找不到memcachedProxy对象. --> <!-- memcached 初始化开始 --> <bean id="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder"> <!-- XMemcachedClientBuilder have two arguments.First is server list,and second is weights array. --> <constructor-arg> <list> <bean class="java.net.InetSocketAddress"> <constructor-arg> <value>192.168.1.80</value> </constructor-arg> <constructor-arg> <value>11210</value> </constructor-arg> </bean> </list> </constructor-arg> <property name="connectionPoolSize" value="5"></property> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory"></bean> </property> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder"> <constructor-arg> <value>102400</value> </constructor-arg> </bean> </property> </bean> <bean id="memcachedClient" factory-bean="memcachedClientBuilder" factory-method="build" destroy-method="shutdown" /> <bean id="memcachedManager" class="com.xxxxxx.memcached.MemcachedManager"> <property name="memcachedClient"> <ref bean="memcachedClient"/> </property> </bean> <bean id="memcachedProxy" class="com.xxxxxx.memcached.MemcachedProxy"> <property name="memcachedManager"> <ref bean="memcachedManager"/> </property> </bean> <!-- memcached 初始化结束 -->
5、ibatis配置文件SqlMapConfig.xml中添加对缓存的支持
<settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" />
6、ibatis的sql.xml中使用CacheModel
<cacheModel id="vweb_cache" readOnly="false" serialize="true" type="com.xxxxxx.memcached.MemcachedIbatisController"> <flushInterval hours="24" /> <flushOnExecute statement="insertVweb" /> <flushOnExecute statement="updateVweb" /> <flushOnExecute statement="removeVweb" /> <property name="cache-size" value="1000" /> </cacheModel>
<select id="getVwebList" parameterClass="com.xxxxxx.vweb.model.Vweb" resultClass="com.xxxxxx.vweb.model.Vweb" cacheModel="vweb_cache"> SELECT * FROM wx_vweb <include refid="getVwebList_body" /> <dynamic prepend=""> <isNotNull property="orderCol"> order by $orderCol$ <isNotNull property="ascDesc"> $ascDesc$ </isNotNull> </isNotNull> </dynamic> <dynamic prepend=""> <isNotNull property="rowNumStart"> <isNotNull property="pageSize"> LIMIT #rowNumStart#,#pageSize# </isNotNull> </isNotNull> </dynamic> </select>
7、可以启动tomcat,测试Cache是否生效(注意日志输出)
Get the IBATIS_CACHED_Vweb.vweb_cache_1438835114 from memcached, value=[[email protected]
表示缓存已经生效
8、修改ibatis源码,实现多个tomcat共享缓存
原理:memcache 为KV数据库,所以,保证同一条sql语句,同样的查询条件,在缓存时,key值一样,就实现了缓存共享
ibatis的CacheKey生成,不懂的可以百度
com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.java中最终决定CacheKey的代码
/** * Add a mapped statement * * @param ms - the mapped statement to add */ public void addMappedStatement(MappedStatement ms) { if (mappedStatements.containsKey(ms.getId())) { throw new SqlMapException("There is already a statement named " + ms.getId() + " in this SqlMap."); } ms.setBaseCacheKey(hashCode()); mappedStatements.put(ms.getId(), ms); }
ms.setBaseCacheKey(hashCode()); 这句代码决定了basekey的生成hashCode方法的实现如下
public int hashCode() { CacheKey key = new CacheKey(); if (txManager != null) { key.update(txManager); if (txManager.getDataSource() != null) { key.update(txManager.getDataSource()); } } //key.update(System.identityHashCode(this)); key.update(DEFAULT_SYS_HASHCODE); return key.hashCode(); }
注释掉的部分为原来的实现,这也就导致了不同的tomcat实例,basekey不一致
把代码进行替换,hashCode生成为固定值,保证多个tomcat中CacheKey一致
实现缓存共享
9,、替换jar包中的class文件,启动多个tomcat验证缓存的共享
时间: 2024-10-17 00:55:57