原創文章,請尊重作者的辛勤勞動,轉載請注明!
本人參考文檔:http://blog.csdn.net/ajun_studio/article/details/7343781
memcached批量刪除解決方案:http://tech.ddvip.com/2008-10/122405933178234.html
(2013-4-10續 本人實現了不用數據庫臨時表的方法,見 利用Spring AOP 更新memcached 緩存策略的實現(二))
這里實現的是“LogDB方案”,及用數據庫的一個表記錄memcached的實現
實現思路:
1. 查詢數據時,先查看memcached中是否存在要查找結果,如果存在讀取memcached,不存在,從數據庫中讀取,並存入memcached中
2. 以一個list<bean>為例,如果修改其中一個person的數據,則刪除相關list信息,修改學生數據,再重新存儲memcached數據
3. 利用spring-aop和注解實現以上邏輯,做到對業務邏輯代碼的解耦
具體實現步驟如下:
1. 配置spring-aop,因為我這里還用的是spring-mvc框架,所以引用比較多,spring版本為spring-framework-3.2.1.RELEASE
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 引入jdbc配置文件 --> <context:property-placeholder location="classpath*:jdbc.properties"/> <!--創建jdbc數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${driverClass}"/> <property name="jdbcUrl" value="${jdbcUrl}"/> <property name="user" value="${user}"/> <property name="password" value="${password}"/> <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。--> <property name="initialPoolSize" value="${initialPoolSize}"/> <!--連接池中保留的最小連接數。--> <property name="minPoolSize" value="${minPoolSize}"/> <!--連接池中保留的最大連接數。--> <property name="maxPoolSize" value="${maxPoolSize}"/> <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。--> <property name="maxIdleTime" value="${maxIdleTime}"/> <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。--> <property name="acquireIncrement" value="${acquireIncrement}"/> <!--每60秒檢查所有連接池中的空閑連接。--> <property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 可通過注解控制事務 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 自動掃描bean,把作了注解的類轉換為bean --> <context:component-scan base-package="com.dsideal" /> <!-- 加載組裝所以配置文件 context:component-scan注冊后可以省略當前配置 <context:annotation-config /> --> <!-- 通過注解,把URL映射到Controller上,該標簽默認注冊RequestMappingHandlerMapping和RequestMappingHandlerAdapter --> <mvc:annotation-driven /> <!-- 啟用AOP注解 --> <aop:aspectj-autoproxy/> <!-- 視圖解析器 InternalResourceViewResolver:支持jsp和jstl解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 使用JSP頁面進行輸出 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <!-- 指定了表示層的前綴 --> <property name="prefix" value="/WEB-INF/bizroot/" /> <!-- 指定了表示層的后綴 --> <property name="suffix" value=".jsp" /> </bean> <!-- 處理文件上傳處理 maxUploadSize:最大上傳限制 uploadTempDir:上傳臨時路徑,文件上傳完成后,臨時目錄中的臨時文件會被自動清除 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8" p:maxUploadSize="5242880" /> <!-- Session,角色權限的攔截器 黃海添加於2013-03-15--> <mvc:interceptors> <bean class="com.dsideal.interceptor.SystemInterceptor" /> </mvc:interceptors> </beans>
2. 下載memcached的jar包,相關方法請參考本人之前的博客:這里
3. 編寫兩個注解,分別為@Cache和@Flush
package com.dsideal.common; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 用於查找的時候,放置緩存信息 * @author Administrator * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Cache { //key的前綴 String prefix(); //緩存有效期 1000*60*60*2=2小時 long expiration() default 1000*60*60*2; }
package com.dsideal.common; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 用於刪除緩存 * @author Administrator * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface Flush { //key的前綴 String prefix(); }
4. 數據庫端創建臨時表存儲memecached的key值,prefix:包名前綴;cache_key:存入memcached的key(包名前綴+參數類型+參數值轉MD5)
注:這里cache_key這樣存儲的原因是防止查找key時發生混亂,比如兩個參數,1個為年級,1個為班級,兩個如果有一個為空的時候,就會產生key值重復的情況;
轉存MD5是怕參數過多,key過長
CREATE TABLE `t_cache_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `prefix` varchar(20) DEFAULT NULL, `cache_key` varchar(300) DEFAULT NULL, `add_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8;
5. memcached的配置類為
package com.dsideal.sys.memcached; import java.util.Date; import com.danga.MemCached.MemCachedClient; import com.danga.MemCached.SockIOPool; public class MemcacheBlog { // 創建全局的唯一實例 static MemCachedClient memCachedClient=null; // 設置與緩存服務器的連接池 static{ //memcached服務器端IP和端口 String[] servers = {"192.168.100.102:11211"}; SockIOPool pool = SockIOPool.getInstance(); pool.setServers(servers); pool.setFailover(true); // 設置初始連接數、最小和最大連接數以及最大處理時間 /* pool.setInitConn(5); pool.setMinConn(5); pool.setMaxConn(250); pool.setMaxIdle(1000 * 60 * 60 * 6); */ pool.setInitConn(10); pool.setMinConn(5); pool.setMaxConn(250); pool.setMaintSleep(30); // 設置主線程的睡眠時間 // 設置TCP的參數,連接超時等 pool.setNagle(false); pool.setSocketTO(3000); pool.setAliveCheck(true); pool.initialize(); memCachedClient = new MemCachedClient(); memCachedClient.setPrimitiveAsString(true); } /** * <p>功能: * get:獲取數據的方法 * get_multi:一次取得多條數據;getmulti可以非同步地同時取得多個鍵值, 其速度要比循環調用get快數十倍 * </p> * @author 周楓 * @date 2013-4-3 * @param * @return Object */ public static Object get(String key) { return memCachedClient.get(key); } /** * <p>功能: * add:僅當存儲空間中不存在鍵相同的數據時才保存 * replace:僅當存儲空間中存在鍵相同的數據時才保存 * set:與add和replace不同,無論何時都保存</p> * @author 周楓 * @date 2013-4-3 * @param * @return Object */ public static boolean set(String key,Object o) { return memCachedClient.set(key, o); } public static boolean set(String key,Object o,Date ExpireTime) { return memCachedClient.set(key, o, ExpireTime); } public static boolean exists(String key) { return memCachedClient.keyExists(key); } public static boolean delete(String key) { return memCachedClient.delete(key); } }
6. spring-aop實現類
package com.dsideal.sys.action; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.annotation.Resource; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.dsideal.common.Cache; import com.dsideal.common.Flush; import com.dsideal.sys.bean.CacheLog; import com.dsideal.sys.bean.SysLoginPersonBean; import com.dsideal.sys.memcached.MemcacheBlog; import com.dsideal.sys.service.ICacheLogService; import com.dsideal.common.MD5; @Aspect @Component public class CacheAopBlog { // @Pointcut("execution(* add*(..)) || (execution(* del*(..))) || (execution(* get*(..)))") //* com.dsideal.sys.service.impl.*.getMemcache*(..) @Pointcut("execution (* com.dsideal.sys.service.impl.*.memcache*(..))") public void pointcut(){} //方法執行前調用 //@Before("pointcut()") public void before() { System.out.println("before"); //2 } @Resource private ICacheLogService cacheLogService; //方法執行的前后調用 @Around("pointcut()") //ProceedingJoinPoint 目標類連接點對象 public Object doAround(ProceedingJoinPoint call) throws Throwable{ //返回最終結果 Object result = null; Method[] methods = call.getTarget().getClass().getDeclaredMethods(); Signature signature = call.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); for(Method m:methods){ //循環方法,找匹配的方法進行執行 if(m.getName().equals(method.getName())){ //增加 if(m.isAnnotationPresent(Cache.class)){ Cache cache = m.getAnnotation(Cache.class); Object tempType = m.getGenericReturnType(); //System.out.println(m.get); //如果memcached中存在 if(cache!=null){ //獲取方法名+參數類型+參數值 轉 MD5 String tempKey = this.getKey(method, call.getArgs()); //獲取注解前綴,這里為 sys,實際使用是為各個業務包名 String prefix = cache.prefix(); //存入memcached的最終key值 String key = prefix+"_"+tempKey; result =MemcacheBlog.get(key); if(null == result){ try { //執行aop攔截的方法 result = call.proceed(); //獲取注解配置memcached死亡時間 long expiration = cache.expiration(); //1000*60*60*2==2小時過期 Date d=new Date(); //memcached死亡時間 d=new Date(d.getTime()+expiration); //利用fastjson序列化list<bean>存入memcached中 //具體fastjson使用方法請參考:http://www.cnblogs.com/cczhoufeng/archive/2013/04/03/2997871.html MemcacheBlog.set(key, JSON.toJSONString(result), d); //將key存入數據庫 CacheLog log = new CacheLog(); log.setPrefix(prefix); log.setCache_key(key); //存入臨時表中 this.cacheLogService.add(log); } catch (Throwable e) { e.printStackTrace(); } } else { //如果memcached中存在結果,需要將result反序列化后返回結果 String memresult = result.toString(); //反序列化 List<SysLoginPersonBean> list = JSON.parseArray(memresult, SysLoginPersonBean.class); result = list; //這里是利用fastjson反序列化輸出的方法 //String memresult = result.toString(); //List<SysLoginPersonBean> list = JSON.parseArray(memresult, SysLoginPersonBean.class); //for (int i = 0; i < list.size(); i++) { // System.out.println(list.get(i).getReal_name()); //} } } } else if(m.isAnnotationPresent(Flush.class)){ Flush flush = m.getAnnotation(Flush.class); if(flush!=null){ result = call.proceed(); String prefix = flush.prefix(); List<CacheLog> logs = cacheLogService.findListByPrefix(prefix); if(logs!=null && !logs.isEmpty()){ //刪除數據庫 int rows = cacheLogService.deleteByPrefix(prefix); if(rows>0){ for(CacheLog log :logs){ if(log!=null){ String key = log.getCache_key(); MemcacheBlog.delete(key);//刪除緩存 } } } } } }else{ try { result = call.proceed(); } catch (Throwable e) { e.printStackTrace(); } } break; } } return result; } /** * 組裝key值 * @param method * @param args * @return */ private String getKey(Method method, Object [] args){ StringBuffer sb = new StringBuffer(); //獲取方法名 String methodName = method.getName(); //獲取參數類型 Object[] classTemps = method.getParameterTypes(); //存入方法名 sb.append(methodName); for (int i = 0; i < args.length; i++) { sb.append(classTemps[i]+"&"); if (null == args[i]) { sb.append("null"); } else if ("".equals(args[i])) { sb.append("*"); } else { sb.append(args[i]); } } return MD5.getMD5(sb.toString()); } }
7. MD5的實現類
package com.dsideal.common; import java.security.MessageDigest; public class MD5 { public static String getMD5(String sourceStr) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { byte[] strTemp = sourceStr.getBytes(); MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(strTemp); byte[] md = mdTemp.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字節中高 4 位的數字轉換, >>> 為邏輯右移,將符號位一起右移 str[k++] = hexDigits[byte0 & 0xf]; // 取字節中低 4 位的數字轉換 } return new String(str); } catch (Exception e) { return null; } } }
8. 我做列子用的表,mysql數據庫
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50529 Source Host : localhost:3306 Source Database : lxyy_db Target Server Type : MYSQL Target Server Version : 50529 File Encoding : 65001 Date: 2013-04-09 10:34:24 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `t_sys_loginperson` -- ---------------------------- DROP TABLE IF EXISTS `t_sys_loginperson`; CREATE TABLE `t_sys_loginperson` ( `person_id` char(36) NOT NULL, `login_name` varchar(128) NOT NULL, `login_pwd` varchar(128) NOT NULL, `b_use` int(11) NOT NULL, `identity_id` int(11) DEFAULT NULL, `bureau_id` int(11) DEFAULT NULL, `real_name` varchar(128) DEFAULT NULL, PRIMARY KEY (`person_id`), UNIQUE KEY `ix_t_sys_loginperson` (`login_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_sys_loginperson -- ---------------------------- INSERT INTO `t_sys_loginperson` VALUES ('0E04DE60-7264-4FE7-9A6C-5AB843B603CC', 'czls', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中歷史管理員1'); INSERT INTO `t_sys_loginperson` VALUES ('0E324A5B-981B-40A6-8AFF-1B3D55E486E6', 'gzhx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中化學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('2383E58E-2488-4C1F-A262-3BE64C87A12A', 'gzdl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中地理管理員'); INSERT INTO `t_sys_loginperson` VALUES ('2D53739E-B2EF-4CC2-BA93-8355EF30528C', 'czyinyue', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中音樂管理員'); INSERT INTO `t_sys_loginperson` VALUES ('3C9B016C-5E30-46B0-A401-A434108D6725', 'czsw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中生物管理員'); INSERT INTO `t_sys_loginperson` VALUES ('3FDC5489-6B70-11E2-B11E-00FF2D04A858', 'xxsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小學數學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('524C0D25-CC64-4C34-A168-F8A6FB2FA4AC', 'czwl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中物理管理員'); INSERT INTO `t_sys_loginperson` VALUES ('9113D74A-0292-4EBB-8C52-77E71F5AFC16', 'xxyinyue', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小學音樂管理員'); INSERT INTO `t_sys_loginperson` VALUES ('9210EE35-51B3-412B-BFF9-9CDB684A3B2F', 'czhx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中化學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('974E3FB8-04D0-42F2-9B8A-5D942C2B954F', 'xxyingyu', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小學英語管理員'); INSERT INTO `t_sys_loginperson` VALUES ('A9609550-7B82-4780-BC5D-B172BCA32230', 'xxkx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小學科學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('A9AAEB9E-ADD4-425A-8FCF-241E9A9C12EC', 'gzwl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中物理管理員'); INSERT INTO `t_sys_loginperson` VALUES ('D26A1BFC-7996-408B-8157-0CFB77A9D427', 'czsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中數學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('D43EA9DD-1EDA-437B-A012-CF35238B750E', 'gzsx', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中數學管理員'); INSERT INTO `t_sys_loginperson` VALUES ('D6EC9FBB-1C3C-40EC-8D18-7CD48A7B9CA3', 'gzsw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '高中生物管理員'); INSERT INTO `t_sys_loginperson` VALUES ('E49F1620-F6AA-42BB-96A8-198F642FCE59', 'xxyw', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '小學語文管理員'); INSERT INTO `t_sys_loginperson` VALUES ('F38A7B0C-A292-4690-8D2D-EC7CC5E4F069', 'czdl', 'c4ca4238a0b923820dcc509a6f75849b', '1', '1', '1', '初中地理管理員');
9. 生成bean
package com.dsideal.sys.bean; @SuppressWarnings("serial") public class SysLoginPersonBean { private static final long serialVersionUID = 7517080513591583073L; private String person_id; private String login_name; private String login_pwd; private int b_use; private int identity_id; private int bureau_id; private String real_name; public String getPerson_id() { return person_id; } public void setPerson_id(String person_id) { this.person_id = person_id; } public String getLogin_name() { return login_name; } public void setLogin_name(String login_name) { this.login_name = login_name; } public String getLogin_pwd() { return login_pwd; } public void setLogin_pwd(String login_pwd) { this.login_pwd = login_pwd; } public int getB_use() { return b_use; } public void setB_use(int b_use) { this.b_use = b_use; } public int getIdentity_id() { return identity_id; } public void setIdentity_id(int identity_id) { this.identity_id = identity_id; } public int getBureau_id() { return bureau_id; } public void setBureau_id(int bureau_id) { this.bureau_id = bureau_id; } public String getReal_name() { return real_name; } public void setReal_name(String real_name) { this.real_name = real_name; } public SysLoginPersonBean() { } public SysLoginPersonBean(String person_id, String login_name, String login_pwd, int b_use, int identity_id, int bureau_id, String real_name) { this.person_id = person_id; this.login_name = login_name; this.login_pwd = login_pwd; this.b_use = b_use; this.identity_id = identity_id; this.bureau_id = bureau_id; this.real_name = real_name; } }
10. 接口文件 ICacheLogService
package com.dsideal.sys.service; import java.util.List; import com.dsideal.sys.bean.CacheLog; import com.dsideal.sys.bean.SysLoginPersonBean; public interface ICacheLogService { /** * <p>功能:增加memcached數據文件到臨時表中</p> * @author 周楓 * @date 2013-4-9 * @param * @return void */ public void add(CacheLog log); /** * <p>功能:查詢以prefix為前綴的所有key值,在更新刪除時使用此方法</p> * @author 周楓 * @date 2013-4-9 * @param * @return List<CacheLog> */ public List<CacheLog> findListByPrefix(String prefix); /** * <p>功能:刪除操作時,aop攔截</p> * @author 周楓 * @date 2013-4-9 * @param * @return void */ public void memcacheDeleteByPrefix(); /** * <p>功能:刪除臨時表記錄的數據</p> * @author 周楓 * @date 2013-4-9 * @param * @return int */ public int deleteByPrefix(String prefix); /** * <p>功能:查找例子,查找所有人員數據,后面的person_id沒有使用,只是為了測試key值的生成策略</p> * @author 周楓 * @date 2013-4-9 * @param * @return List<SysLoginPersonBean> */ public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id); /** * <p>功能:測試方法,可以忽略</p> * @author 周楓 * @date 2013-4-9 * @param * @return List<CacheLog> */ public List<CacheLog> memcacheCacheLogFindAll(); /** * <p>功能:修改人員</p> * @author 周楓 * @date 2013-4-9 * @param * @return int */ public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name); }
11. 接口實現類,"sys"為業務包名,為key的前綴,expiration:自定義memcached死亡時間
package com.dsideal.sys.service.impl; import java.util.List; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.dsideal.common.Cache; import com.dsideal.common.Flush; import com.dsideal.sys.bean.CacheLog; import com.dsideal.sys.bean.SysLoginPersonBean; import com.dsideal.sys.dao.CacheLogDao; import com.dsideal.sys.service.ICacheLogService; @Service public class CacheLogServiceImpl implements ICacheLogService { @Resource private CacheLogDao dao; @Override public void add(CacheLog log) { dao.add(log); } @Override public List<CacheLog> findListByPrefix(String prefix) { // TODO Auto-generated method stub return dao.findListByPrefix(prefix); } @Override @Flush(prefix="sys") public void memcacheDeleteByPrefix() { // TODO Auto-generated method stub } @Override public int deleteByPrefix(String prefix) { // TODO Auto-generated method stub return dao.deleteByPrefix(prefix); } @Override @Cache(prefix="sys",expiration=1000*60*60*2) public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id) { // TODO Auto-generated method stub return dao.findAll(b_use); } @Override @Cache(prefix="sys",expiration=1000*60*60*2) public List<CacheLog> memcacheCacheLogFindAll() { // TODO Auto-generated method stub return dao.findCacheLogAll(); } @Override @Flush(prefix="sys") public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name) { return dao.updateSysLoginPersonBean(person_id,real_name); } }
12. spring的jdbctemplate實現dao層
package com.dsideal.sys.dao; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Repository; import java.util.Date; import java.util.List; import com.dsideal.common.BaseDao; import com.dsideal.common.Utils.RSMapper; import com.dsideal.sys.bean.CacheLog; import com.dsideal.sys.bean.SysLoginPersonBean; @Repository public class CacheLogDao extends BaseDao { public void add(CacheLog log) { try { String sql = "INSERT INTO t_cache_log(prefix,cache_key,add_time) VALUES (?,?,?)"; int result = 0; Date now = new Date(); result = this.jdbcTemplate.update(sql, log.getPrefix(),log.getCache_key(),now); System.out.println("增加成功"); } catch (DataAccessException e) { e.printStackTrace(); System.out.println("增加報錯"); } } public int deleteByPrefix(String prefix) { try { String sql = "DELETE FROM t_cache_log WHERE prefix = ?"; int result = 0; result = this.jdbcTemplate.update(sql, prefix); return result; } catch (DataAccessException e) { e.printStackTrace(); } return 0; } public List<CacheLog> findListByPrefix(String prefix) { try { String sql = "SELECT * FROM t_cache_log WHERE prefix = ?"; return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class, prefix); } catch (DataAccessException e) { e.printStackTrace(); } return null; } public List<SysLoginPersonBean> findAll(int b_use) { try { String sql = "SELECT * FROM t_sys_loginperson WHERE b_use = ?"; return RSMapper.queryList(jdbcTemplate, sql, SysLoginPersonBean.class,b_use); } catch (DataAccessException e) { e.printStackTrace(); } return null; } public List<CacheLog> findCacheLogAll() { try { String sql = "SELECT * FROM t_cache_log"; return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class); } catch (DataAccessException e) { e.printStackTrace(); } return null; } public int updateSysLoginPersonBean(String person_id,String real_name) { int result = 0; try { String sql = "UPDATE t_sys_loginperson SET real_name = ? WHERE person_id = ?"; return this.jdbcTemplate.update(sql, real_name,person_id); } catch (DataAccessException e) { e.printStackTrace(); System.out.println("修改時報錯"); } return result; } }
13. Service測試類
package com.dsideal.sys.test; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.dsideal.sys.bean.CacheLog; import com.dsideal.sys.bean.SysLoginPersonBean; import com.dsideal.sys.service.ICacheLogService; import com.dsideal.sys.service.impl.CacheLogServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) //指定Spring的配置文件 /為classpath下 @ContextConfiguration(locations = {"/spring-mvc.xml"}) public class CacheLogServiceTest { @Autowired private ICacheLogService impl; @Before //在每個測試用例方法之前都會執行 public void init(){ } @After //在每個測試用例執行完之后執行 public void destory(){ } @Test public void add() { CacheLog log = new CacheLog(); impl.add(log); } @Test public void findAll() { int b_use = 1; String person_id = ""; List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i).getReal_name()); } } @Test public void deleteByPrefix() { System.out.println("1"); impl.memcacheDeleteByPrefix(); System.out.println("刪除成功"); } @Test public void findCacheLogAll() { List<CacheLog> list = impl.memcacheCacheLogFindAll(); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i).getCache_key()); } } @Test public void updateToDeleteToFind() { String prefix = "sys"; String person_id1 = "0E04DE60-7264-4FE7-9A6C-5AB843B603CC"; String person_id2 = ""; String real_name = "初中歷史管理員1"; int result = impl.memcacheupdateSysLoginPersonBean(prefix, person_id1, real_name); if (result > 0) { System.out.println("修改成功"); } int b_use = 1; List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id2); System.out.println("查詢成功"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i).getReal_name()); } } }
具體測試方法為:
1. 當memcached服務器端和臨時表均為空時,測試類中執行 findAll() 方法,進入aop攔截"doAround"方法,添加當前數據list<SysLoginPersonBean>進入memcached中,並在臨時表記錄當前key值
2. 再次執行"findAll()"方法,跟蹤測試,查找結果將從memcached中查找結果並反序列化后返回結果
3. 當修改人員中一人時,執行"updateToDeleteToFind()"方法,這里的測試方法,我是為了測試而寫,所以先修改了人員的數據,再次執行了一遍查詢,查詢同第一條,可以發現,list<SysLoginPersonBean>中的值已經修改,因為我在執行"memcacheupdateSysLoginPersonBean()"方法時,加入了@Flush注解,再修改的時候,aop攔截此方法,執行刪除操作,再重新存入數據到memcached和臨時表
PS: 具體測試方法就是以上的步驟了,理論知識感謝開篇引用的博客兩位大神,我正在研究不用數據庫表實現的方法,實現之后會再貼出來的