Memcached 作用與使用 基本介紹

主要從事網(wǎng)頁(yè)設(shè)計(jì)、PC網(wǎng)站建設(shè)(電腦版網(wǎng)站建設(shè))、wap網(wǎng)站建設(shè)(手機(jī)版網(wǎng)站建設(shè))、響應(yīng)式網(wǎng)站開發(fā)、程序開發(fā)、微網(wǎng)站、微信小程序定制開發(fā)等,憑借多年來(lái)在互聯(lián)網(wǎng)的打拼,我們?cè)诨ヂ?lián)網(wǎng)網(wǎng)站建設(shè)行業(yè)積累了豐富的做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、網(wǎng)絡(luò)營(yíng)銷經(jīng)驗(yàn),集策劃、開發(fā)、設(shè)計(jì)、營(yíng)銷、管理等多方位專業(yè)化運(yùn)作于一體,具備承接不同規(guī)模與類型的建設(shè)項(xiàng)目的能力。
1,對(duì)于緩存的存取方式,簡(jiǎn)言之,就是以鍵值對(duì)的形式將數(shù)據(jù)保存在內(nèi)存中。在日常業(yè)務(wù)中涉及的操作無(wú)非就是增刪改查。加入緩存機(jī)制后,查詢的時(shí)候,對(duì)數(shù)據(jù)進(jìn)行緩存,增刪改的時(shí)候,清除緩存即可。這其中對(duì)于緩存的閉合就非常重要,如果緩存沒(méi)有及時(shí)得到更新,那用戶就會(huì)獲取到過(guò)期數(shù)據(jù),就會(huì)產(chǎn)生問(wèn)題。
2,對(duì)于單一業(yè)務(wù)的緩存管理(數(shù)據(jù)庫(kù)中只操作單表),只需生成一個(gè)key,查詢時(shí),使用key,置入緩存;增刪改時(shí),使用key,清除緩存。將key與表綁定,操作相對(duì)簡(jiǎn)單。
3,但是在現(xiàn)實(shí)業(yè)務(wù)中,更多的是對(duì)關(guān)聯(lián)表的增刪改查(數(shù)據(jù)庫(kù)多表操作),業(yè)務(wù)之間互相關(guān)聯(lián),數(shù)據(jù)庫(kù)中的某張表不止一個(gè)業(yè)務(wù)再進(jìn)行操作,將緩存攔截在service層,對(duì)業(yè)務(wù)進(jìn)行緩存,對(duì)多表進(jìn)行緩存。
4,業(yè)務(wù)層緩存實(shí)現(xiàn)策略:
4.1,在緩存中建立一個(gè)key為"union_query",value為“hashmap<prefix,uqversion>(‘簡(jiǎn)稱uqmap’)“的緩存,prefix保存的是當(dāng)前業(yè)務(wù)操作涉及到的數(shù)據(jù)庫(kù)表名的組合(數(shù)據(jù)庫(kù)表名的唯一性),使用‘|’分隔(例 prefix=“A|B”,此次業(yè)務(wù)將操作A表與B表),uqversion是業(yè)務(wù)版本號(hào),從0開始遞增。
4.2,調(diào)用一個(gè)查詢業(yè)務(wù)時(shí),對(duì)數(shù)據(jù)進(jìn)行緩存,設(shè)置operation為1,告訴cache對(duì)象,這是一個(gè)緩存操作,例如調(diào)用 queryAB(args[])方法時(shí),cache對(duì)象切入,將prefix(即”A|B“)與uqversion(初始化為0),存入uqmap中進(jìn)行緩存。
4.3,將prefix,uqversion,方法明+參數(shù),進(jìn)行拼接,使用md5進(jìn)行加密后作為一個(gè)key,將方法的結(jié)果集作為value,進(jìn)行緩存。至此緩存成功。
4.4,當(dāng)?shù)诙€(gè)請(qǐng)求來(lái)調(diào)用queryAB時(shí),cache對(duì)象切入,首先,查詢uqmap對(duì)象,使用prefix找到對(duì)應(yīng)的uqversion,然后,通過(guò)拼接加密獲取key,最后取得結(jié)果集進(jìn)行返回。
4.5,當(dāng)有一個(gè)updateA方法被調(diào)用時(shí),設(shè)置operation為4,告訴cache對(duì)象,這是一個(gè)刪除緩存的操作,此時(shí)prefix的值為“A”,cache對(duì)象切入,獲取全局的uqmap,遍歷其中的prefix,是否包含了表A的名稱:如果包含,則更新此prefix的uqversion進(jìn)行自增,uqversion一旦發(fā)生變化,4.3中組合的key將不復(fù)存在,業(yè)務(wù)緩存也就消失了。(對(duì)于復(fù)雜的updateAB方法,遍歷prefix要復(fù)雜一點(diǎn),可以實(shí)現(xiàn))
4.6,當(dāng)?shù)谌齻€(gè)請(qǐng)求來(lái)調(diào)用queryAB時(shí),可以獲取到uqversion,組合成key后,但是沒(méi)有對(duì)應(yīng)的value。此時(shí)確定緩存不存在時(shí),繼續(xù)正常執(zhí)行方法,獲取結(jié)果集,返回給客戶的同時(shí),將結(jié)果集進(jìn)行緩存。
5,對(duì)于緩存的操作,網(wǎng)上有三種api可以選擇(memcached client forjava、spymemcached、xmemcached),具體的好壞,本人在這就不做分析。本人使用的是XMemcached api。
具體實(shí)現(xiàn)細(xì)節(jié):
1,新建 @interface Annotation{ } 定義一個(gè)注解 @Annotation,一個(gè)注解是一個(gè)類。定義緩存策略。
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;
/**
* 用于查找的時(shí)候,放置緩存信息
* @author shufeng
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface XmemCache{
/**
* 值為當(dāng)前操作的表名,表名唯一
* 涉及到多表操作,使用|分隔
*/
String prefix() default "";
/*
* 緩存有效期 設(shè)置,單位為秒
* 指定間隔時(shí)間,默認(rèn)值為3600秒(1小時(shí))
* */
int interval() default 3600;
/**
* 1 從cache里取值,如果未置入cache,則置入
* 2 replace cache value 未擴(kuò)展
* 3 replace cache value,并返回舊值 未擴(kuò)展
* 4 remove cache key 從cache里刪除對(duì)應(yīng)的緩存
* 5 remove cache key 從cache里刪除對(duì)應(yīng)的緩存,并返回未刪除之前的值 未擴(kuò)展
**/
int operation() default 1;
}2,memcache基礎(chǔ)操作類,對(duì)一些常用方法進(jìn)行封裝,對(duì)memcachedclient進(jìn)行配置
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import com.node.hlhw.rbac.api.constant.Constant;
import net.rubyeye.xmemcached.GetsResponse;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.command.BinaryCommandFactory;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator;
import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
import net.rubyeye.xmemcached.utils.AddrUtil;
/**
* @author Melody shufeng
* 對(duì)memcachedclient進(jìn)行封裝,添加一下常用方法
*/
public class MemcachedOperate implements DisposableBean {
/*
* timeout - Operation timeout,if the method is not returned in this
* time,throw TimeoutException timeout - operation timeout,in milliseconds
* exp - An expiration time, in seconds. Can be up to 30 days. After 30
* days, is treated as a unix timestamp of an exact date. value - stored
* data
*/
private static final Logger logger = LoggerFactory.getLogger(MemcachedOperate.class);
private static Properties PROPERTIES = new Properties();
private static String MEMCACHED_SETTING = "memcached.properties";
private static MemcachedClient memcachedClient;
public static MemcachedClient getClient(){
return memcachedClient;
}
/**
* 靜態(tài)代碼塊,類加載時(shí),初始化緩存客戶端
* 確保只創(chuàng)建一個(gè)client實(shí)例
* author shufeng
*/
static {
InputStream in = Object.class.getResourceAsStream("/" + MEMCACHED_SETTING);
try {
PROPERTIES.load(in);
} catch (IOException e) {
e.printStackTrace();
}
String servers = PROPERTIES.getProperty("memcached.servers", "");
if (null != servers && !"".equals(servers)) {
try {
logger.debug("啟動(dòng)memcached連接");
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(servers));
builder.setConnectionPoolSize(100);
builder.setFailureMode(true);
builder.setCommandFactory(new BinaryCommandFactory());
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
builder.setTranscoder(new SerializingTranscoder());
memcachedClient = builder.build();
memcachedClient.setEnableHeartBeat(false); // 關(guān)閉心跳
memcachedClient.flushAll(); // 清空緩存
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @param key
* @return 獲取value
*/
public static Object get(String key) {
Object object = null;
try {
object = memcachedClient.get(key);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
return object;
}
public static void setWithNoReply(String key, int exp, Object value) {
try {
memcachedClient.setWithNoReply(key, exp, value);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
}
/**
* 查詢聯(lián)表的業(yè)務(wù)版本號(hào) 如果為空,則初始化
*
* @param prefix
* @return
*/
@SuppressWarnings("unchecked")
public static Long getUnionQueryVersion(String prefix) {
try {
Map<String, Long> uqmap = null;
GetsResponse<Object> getsresponse = memcachedClient.gets(Constant.UNION_QUERY);
if (getsresponse == null) {
uqmap = new HashMap<String, Long>();
Long uqversion = new Long(1); // 初始化版本號(hào)
uqmap.put(prefix, uqversion);
if (memcachedClient.cas(Constant.UNION_QUERY, 0, uqmap, 0)) { // 檢測(cè)插入之前是否被修改過(guò)
return uqversion; // 插入成功
} else { // 插入失敗,說(shuō)明在代碼運(yùn)行期間,已經(jīng)有其他線程去修改了unionquery的緩存,重新進(jìn)行查詢
return getUnionQueryVersion(prefix);
}
} else {
long cas = getsresponse.getCas();
Object uqobj = getsresponse.getValue();
if (uqobj == null) { // 不存在對(duì)象
uqmap = new HashMap<String, Long>();
Long uqversion = new Long(1); // 初始化版本號(hào)
uqmap.put(prefix, uqversion);
if (memcachedClient.cas(Constant.UNION_QUERY, 0, uqmap, cas)) { // 檢測(cè)插入之前是否被修改過(guò)
return uqversion; // 插入成功
} else { // 插入失敗,說(shuō)明在代碼運(yùn)行期間,已經(jīng)有其他線程去修改了unionquery的緩存,重新進(jìn)行查詢
return getUnionQueryVersion(prefix);
}
} else {
uqmap = (Map<String, Long>) uqobj;
Long uqversion = uqmap.get(prefix);
if (uqversion == null) { // 不存在此業(yè)務(wù)版本
uqversion = new Long(1); // 初始化版本號(hào)
uqmap.put(prefix, uqversion);
if (memcachedClient.cas(Constant.UNION_QUERY, 0, uqmap, cas)) { // 檢測(cè)插入之前是否被修改過(guò)
return uqversion; // 插入成功
} else { // 插入失敗,說(shuō)明在代碼運(yùn)行期間,已經(jīng)有其他線程去修改了unionquery的緩存,重新進(jìn)行查詢
return getUnionQueryVersion(prefix);
}
} else {
return uqversion;
}
}
}
} catch (TimeoutException | InterruptedException | MemcachedException e) {
e.printStackTrace();
System.err.println("getUnionQueryVersion---Exception");
}
return 1L;
}
/**
* 查詢單表的業(yè)務(wù)版本號(hào) 如果為空,則初始化
*
* @return
*/
public static Long getVersion(String prefix) {
try {
GetsResponse<Object> getsresponse = memcachedClient.gets(prefix);
if (getsresponse == null) {
Long pfversion = new Long(1);
if (memcachedClient.cas(prefix, 0, pfversion, 0)) {
return pfversion;
} else {
return getVersion(prefix);
}
} else {
Object pfobj = getsresponse.getValue();
long cas = getsresponse.getCas();
if (pfobj == null) {
Long pfversion = new Long(1);
if (memcachedClient.cas(prefix, 0, pfversion, cas)) {
return pfversion;
} else {
return getVersion(prefix);
}
} else {
return (Long) pfobj;
}
}
} catch (TimeoutException | InterruptedException | MemcachedException e) {
e.printStackTrace();
System.err.println("getVersion---Exception");
}
return 1L;
}
/**
* shufeng 更新 多表版本號(hào)
* 由于存在線程安全問(wèn)題 ,會(huì)覆蓋uqmap,更新unionquery業(yè)務(wù)版本號(hào)
* 使用cas方法解決線程安全問(wèn)題
* 更新unionquery中key包含p1或p2或p3的version
* @param prefix
*/
@SuppressWarnings("unchecked")
public static void updateUnionQueryVersion(String prefix) {
try {
Map<String, Long> uqmap = null;
GetsResponse<Object> getsresponse = memcachedClient.gets(Constant.UNION_QUERY);
if (getsresponse == null) {
return;
} else {
Object uqobj = getsresponse.getValue();
long cas = getsresponse.getCas();
if (uqobj == null) {
return;
} else {
uqmap = (HashMap<String, Long>) uqobj;
Set<String> uqset = uqmap.keySet(); // 遍歷unionquery中的key
Iterator<String> quit = uqset.iterator();
String uqkey = "";
boolean uqflag = false;
while (quit.hasNext()) {
uqkey = quit.next();
if (("|" + uqkey + "|").contains("|" + prefix + "|")) { // key中包含prefix
uqmap.put(uqkey, uqmap.get(uqkey) + 1); // 更新map
uqflag = true;
}
}
if (uqflag) {
if (!memcachedClient.cas(Constant.UNION_QUERY, 0, uqmap, cas)) {
updateUnionQueryVersion(prefix);
}
}
}
}
} catch (TimeoutException | InterruptedException | MemcachedException e) {
e.printStackTrace();
System.err.println("updateUnionQueryVersion---Exception");
}
}
/**
* 更新單表版本號(hào)
*
* @param prefix
* @return
*/
public static void updateVersion(String prefix) {
try {
GetsResponse<Object> getsresponse;
getsresponse = memcachedClient.gets(prefix);
if (getsresponse == null) {
return ;
} else {
Object pfobj = getsresponse.getValue();
long cas = getsresponse.getCas();
if (pfobj == null) {
return ;
} else {
Long pfversion = (Long) pfobj;
pfversion += 1;
if (!memcachedClient.cas(prefix, 0, pfversion, cas)) {
updateVersion(prefix);
}
}
}
} catch (TimeoutException | InterruptedException | MemcachedException e) {
e.printStackTrace();
System.err.println("updateVersion---Exception");
}
}
public void shutdown() {
try {
memcachedClient.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void destroy() throws Exception {
shutdown();
}
}3,結(jié)合spring aop 配置緩存,使用spring aop來(lái)切入業(yè)務(wù)層加入緩存,與業(yè)務(wù)進(jìn)行解耦。使用注解進(jìn)行方便配置。
import java.lang.reflect.Method;
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.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.node.hlhw.common.cache.XmemCache;
import com.node.hlhw.common.digest.Md5Utils;
@Component
@Aspect
public class MemcachedAop {
@Pointcut("execution (* com.node.hlhw.*.service.impl.*.*(..))")
public void pointcut() {
}
// 方法執(zhí)行前調(diào)用
@Before("pointcut()")
public void before() {
}
// 方法執(zhí)行的前后調(diào)用
/**
*
* 改進(jìn)建議:使用uuid作為版本號(hào),減少版本號(hào)的讀取,直接生成uuid,進(jìn)行緩存
* 線程安全問(wèn)題:存在線程安全問(wèn)題,但是針對(duì)于緩存,問(wèn)題不大。
* 多線程同一時(shí)間重復(fù)覆蓋一個(gè)業(yè)務(wù)id,還是可以更新緩存
*
* @param call
* @throws Throwable
*/
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint call) throws Throwable {
Object result = null;
// 檢測(cè)是否存在memcached客戶端實(shí)例
if (MemcachedOperate.getClient() == null) {
System.err.println("memcached client not exist");
result = call.proceed();
return result;
}
Signature signature = call.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if(!method.isAnnotationPresent(XmemCache.class)){
result = call.proceed();
return result;
}
XmemCache xmemcache = method.getAnnotation(XmemCache.class);
// 獲取操作方法
int operation = xmemcache.operation();
// 獲取注解前綴,實(shí)際使用是為各個(gè)業(yè)務(wù)包名稱,一般為表名
String prefix = xmemcache.prefix();
// 無(wú)前綴
if(prefix==null||"".equals(prefix)){
result = call.proceed();
return result;
}
// 獲取注解配置memcached死亡時(shí)間 秒單位
int interval = xmemcache.interval();
switch (operation) {
case 1: // 1 從cache里取值,如果未置入cache,則置入
// 判斷prefix是否涉及多表,查看是否包含|
if (prefix.contains("|")) {
Long uqversion = MemcachedOperate.getUnionQueryVersion(prefix);
String combinedkey = generCombinedKey(prefix, uqversion, method, call.getArgs());
Object resultobj = MemcachedOperate.get(combinedkey);
if(resultobj == null){
result = call.proceed();
MemcachedOperate.setWithNoReply(combinedkey, interval, JSON.toJSONString(result));// 緩存數(shù)據(jù)
}else{
Class<?> returnType = ((MethodSignature) signature).getReturnType();
result = JSON.parseObject(resultobj.toString(), returnType);
}
} else { // 單表操作
Long pfversion = MemcachedOperate.getVersion(prefix);
String combinedkey = generCombinedKey(prefix, pfversion, method, call.getArgs());
Object resultobj = MemcachedOperate.get(combinedkey);
if(resultobj == null){
result = call.proceed();
MemcachedOperate.setWithNoReply(combinedkey, interval, JSON.toJSONString(result));// 緩存數(shù)據(jù)
}else{
Class<?> returnType = ((MethodSignature) signature).getReturnType();
result = JSON.parseObject(resultobj.toString(), returnType);
}
}
break;
case 2: // 2 replace cache value
break;
case 3:
break;
case 4: // 4 remove cache key 從cache里刪除對(duì)應(yīng) 業(yè)務(wù)版本的緩存
/*
* 更新unionquery業(yè)務(wù)版本號(hào)
* 0,切割 prefix為p1、p2、p3
* 1,更新prefix為p1或p2或p3的version
* 2,更新unionquery中key包含p1或p2或p3的version
*/
if (prefix.contains("|")) { // 表示涉及到多表,需要清除 單表的緩存,與聯(lián)表中 包含 當(dāng)前部分的 緩存
String[] prefixs = prefix.split("\\|"); // 0.切割 prefix為p1、p2、p3
for(String pf : prefixs){
MemcachedOperate.updateVersion(pf); // 1,更新prefix為p1或p2或p3的version
MemcachedOperate.updateUnionQueryVersion(pf);
}
}else{ // 沒(méi)有涉及到多表的時(shí)候
MemcachedOperate.updateVersion(prefix);
MemcachedOperate.updateUnionQueryVersion(prefix);
}
result = call.proceed();
break;
default:
result = call.proceed();
break;
}
return result;
}
/**
* 組裝key值
* @param key
* @param version
* @param method
* @param args
* @return
*/
private String generCombinedKey(String key, Long version, Method method, Object[] args) {
StringBuffer sb = new StringBuffer();
// 獲取方法名
String methodName = method.getName();
// 獲取參數(shù)類型
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 {
String tt = JSON.toJSONString(args[i]);
sb.append(tt);
}
}
sb.append(key);
sb.append(version.toString());
String temp = Md5Utils.getMD5(sb.toString());
return temp;
}
}4,properties文件中配置memcached服務(wù)器地址
#host1:port1,host2:port2 memcached.servers=192.168.1.1:11211,192.168.1.2:11211
5,修改spring配置文件,聲明自動(dòng)為spring容器中那些配置@aspectJ切面的bean創(chuàng)建代理,織入切面。
<aop:aspectj-autoproxy proxy-target-class="true"/>
6,service層使用注解方式切入緩存
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.annotation.Service;
import com.node.hlhw.common.cache.XmemCache;
import com.node.hlhw.common.digest.ApplicationUtils;
import com.node.hlhw.core.service.BaseService;
import com.node.hlhw.core.store.IBaseStore;
import com.node.hlhw.core.store.PageParam;
import com.node.hlhw.rbac.api.dao.UserRoleDao;
import com.node.hlhw.rbac.api.entity.UserRole;
import com.node.hlhw.rbac.api.service.UserRoleService;
/**
* @author Melody
* 處理用戶角色
*/
@Service(version = "1.0.0")
public class UserRoleServiceImpl extends BaseService<UserRole> implements
UserRoleService {
private static final Logger logger = Logger
.getLogger(UserRoleServiceImpl.class);
@Autowired
public UserRoleDao userRoleDao;
@Override
protected IBaseStore<UserRole> getBaseDao() {
return userRoleDao;
}
/*
* 單表操作,prefix為表名,operation為4,只進(jìn)行緩存的刪除操作
*/
@XmemCache(prefix="userrole",operation=4)
public void insertUserRole(UserRole userRole) throws Exception {
userRoleDao.insertUserRole(userRole);
logger.info("插入用戶角色數(shù)據(jù)");
}
/* (non-Javadoc)
* 此方法操作了兩個(gè)表,role與userole,使用‘|’進(jìn)行分隔
* operation為1,表示緩存操作,對(duì)結(jié)果集進(jìn)行緩存
* interval表示緩存時(shí)間默認(rèn)不填為3600秒,也可指定具體時(shí)長(zhǎng)
*/
@Override
@XmemCache(prefix="role|userrole",interval=3600 , operation=1)
public List<Map<String, Object>> selectUserRoleList(UserRole userrole, PageParam pageParam) throws Exception {
RowBounds rowBounds = new RowBounds(pageParam.getOffset(),pageParam.getLimit());
List<Map<String, Object>> list = userRoleDao.selectUserRoleList(userrole,rowBounds);
return list ;
}
@Override
@XmemCache(prefix="userrole" , operation=4)
public void modifyUserRole(UserRole userrole, String[] roleids)throws Exception {
//刪除所包含的角色
userRoleDao.deleteByUserRole(userrole);
for(String roleid : roleids){
if(!StringUtils.isEmpty(roleid)){
userrole.setCreatetime(new Date());
userrole.setRoleid(roleid);
userrole.setUuid(ApplicationUtils.getUUID());
userRoleDao.insertUserRole(userrole);
}
}
}
@Override
@XmemCache(prefix="userrole" , operation=1)
public boolean existsRef(String roleids)throws Exception {
String [] roleid = roleids.split(",");
List<String> roleidlist = Arrays.asList(roleid);
return userRoleDao.existsRef(roleidlist)>0?true:false;
}
}
網(wǎng)站名稱:springaop+xmemcached配置service層緩存策略
文章轉(zhuǎn)載:http://www.chinadenli.net/article14/peeede.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、網(wǎng)站設(shè)計(jì)、企業(yè)建站、域名注冊(cè)、用戶體驗(yàn)、網(wǎng)站營(yíng)銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)