公司項目有些地方需要用到緩存,考慮到需要緩存的東西不多,還沒到使用redis memcached 之類的地步,先選擇輕量級的ehcache即可。
項目用到的是mybatis ,本來想用mybatis整合ehcache,但是後來發現一個比他更好點的解決方案,spring 3.1之後就可以整合cache了。
並且使用自帶註解去進行緩存控制非常方便,鬆耦合。
先介紹一下幾個用到的註解
@Cacheable:負責將方法的返回值加入到緩存中
@CacheEvict:負責清除緩存
@Cacheable 支持如下幾個參數:
value:緩存位置名稱,不能爲空,如果使用EHCache,就是ehcache.xml中聲明的cache的name
key:緩存的key,默認爲空,既表示使用方法的參數類型及參數值作爲key,支持SpEL
condition:觸發條件,只有滿足條件的情況纔會加入緩存,默認爲空,既表示全部都加入緩存,支持SpEL
//將緩存保存進andCache,並使用參數中的userId加上一個字符串(這裏使用方法名稱)作爲緩存的key
@Cacheable(value="andCache",key="#userId + 'findById'")
public SystemUser findById(String userId) {
SystemUser user = (SystemUser) dao.findById(SystemUser.class, userId);
return user ;
}
//將緩存保存進andCache,並當參數userId的長度小於32時才保存進緩存,默認使用參數值及類型作爲緩存的key
@Cacheable(value="andCache",condition="#userId.length < 32")
public boolean isReserved(String userId) {
System.out.println("hello andCache"+userId);
return false;
}
@CacheEvict 支持如下幾個參數:value:緩存位置名稱,不能爲空,同上
key:緩存的key,默認爲空,同上
condition:觸發條件,只有滿足條件的情況纔會清除緩存,默認爲空,支持SpEL
allEntries:true表示清除value中的全部緩存,默認爲false
//清除掉指定key的緩存
@CacheEvict(value="andCache",key="#user.userId + 'findById'")
public void EvictUserRole(SystemUser user) {
System.out.println("hello andCache delete"+user.getUserId());
}
//清除掉全部緩存
@CacheEvict(value="andCache",allEntries=true)
public final void EvictUsers(String[] reservedUsers) {
System.out.println("hello andCache deleteall");
}
接下來是具體整合步驟:
一,引入jar包 ehcache-core-2.4.4.jar ehcache-spring-annotations-1.2.0.jar
鏈接:http://pan.baidu.com/s/1mgYpZLI 密碼:op7j
二,配置文件整合
需要spring 容器內支持cache
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
<!-- EhCache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" p:shared="true"/>
</beans>
需要一個ehcache的緩存策略文件。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="3000"
eternal="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="100"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="shopCache"
maxElementsInMemory="3000"
eternal="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="100"
memoryStoreEvictionPolicy="LRU"
/>
<!--
name: cache的名字,用來識別不同的cache,必須惟一。
maxElementsInMemory: 內存管理的緩存元素數量最大限值。
maxElementsOnDisk: 硬盤管理的緩存元素數量最大限值。默認值爲0,就是沒有限制。
eternal: 設定元素是否持久話。若設爲true,則緩存元素不會過期。
overflowToDisk: 設定是否在內存填滿的時候把數據轉到磁盤上。
timeToIdleSeconds: 設定元素在過期前空閒狀態的時間,只對非持久性緩存對象有效。默認值爲0,值爲0意味着元素可以閒置至無限長時間。
timeToLiveSeconds: 設定元素從創建到過期的時間。其他與timeToIdleSeconds類似。
diskPersistent: 設定在虛擬機重啓時是否進行磁盤存儲,默認爲false.(我的直覺,對於安全小型應用,宜設爲true)。
diskExpiryThreadIntervalSeconds: 訪問磁盤線程活動時間。
diskSpoolBufferSizeMB: 存入磁盤時的緩衝區大小,默認30MB,每個緩存都有自己的緩衝區。
memoryStoreEvictionPolicy: 元素逐出緩存規則。共有三種,Recently Used (LRU)最近最少使用,爲默認。 First In First Out (FIFO),先進先出。Less Frequently Used(specified as LFU)最少使用
-->
</ehcache>
package com.shiquanjiumei.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.shiquanjiumei.bean.Shop;
import com.shiquanjiumei.bean.ShopMac;
import com.shiquanjiumei.dao.ShopDao;
import com.shiquanjiumei.service.ShopService;
import com.shiquanjiumei.util.ConstantUtils;
import com.shiquanjiumei.util.PageView;
@Transactional
@Service("shopService")
public class ShopServiceImpl implements ShopService{
@Autowired
private ShopDao shopDao;
@Cacheable(value="shopCache",key="'UserMenuKey3_queryShopsByShopNums'")
@Override
public List<Shop> queryShopsByShopNums(String[] shopNums) {
List<Shop> shopList = new ArrayList<Shop>();
List<Shop> shops = new ArrayList<Shop>();
try {
shopList = shopDao.getShopsByShopNums(shopNums);
for(Shop shop :shopList){
if (!StringUtils.isEmpty(shop.getImageUrl())) {
shop.setImageUrl(ConstantUtils.IMG_URL+shop.getImageUrl()); //設置店鋪Logo地址
shops.add(shop);
}
}
//shopList = shopDao.getShopsByShopNumsDisable(shopNums);
} catch (Exception e) {
e.printStackTrace();
}
return shops;
}
@Cacheable(value="shopCache",key="'UserMenuKey1'+#string")
@Override
public Shop queryShopByShopNum(String string) {
Shop shop = new Shop();
try {
shop = shopDao.queryShopsByShopNum(string);
if(shop!=null){
shop.setImageUrl(ConstantUtils.IMG_URL+shop.getImageUrl());
}
} catch (Exception e) {
e.printStackTrace();
}
return shop;
}
@Cacheable(value="shopCache",key="'UserMenuKey2'+#userid")
@Override
public List<Shop> getShopsByShopNumsDisable(PageView pageView,
String[] macNumArray) {
List<Shop> shopList = new ArrayList<Shop>();
List<Shop> shops = new ArrayList<Shop>();
try {
shopList = shopDao.getShopsByShopNumsDisable(pageView,macNumArray);
for(Shop shop :shopList){
if (!StringUtils.isEmpty(shop.getImageUrl())) {
shop.setImageUrl(ConstantUtils.IMG_URL+shop.getImageUrl()); //設置店鋪Logo地址
shops.add(shop);
}
}
//shopList = shopDao.getShopsByShopNumsDisable(shopNums);
} catch (Exception e) {
e.printStackTrace();
}
return shops;
}
@CacheEvict(value="shopCache",allEntries=true)
@Override
public void add(Shop shop,List<ShopMac> shopMacList) {
shopDao.add(shop);
//shopDao.deleteShopMacByShopNum(shop.getShopNum());
shopDao.addShopMac(shopMacList);
}
/**
* 上傳店鋪Logo
* @param shop
*/
@CacheEvict(value="shopCache",allEntries=true)
@Override
public String uploadShopLogo(Shop shop){
int count = 0;
String logoImageUrlOld = ""; //存放店鋪原Logo路徑
Shop shopOld = new Shop();
try {
String shopNum = shop.getShopNum(); //獲取店鋪編號
shopOld = shopDao.getShopByShopNum(shopNum);
if (null != shopOld) {
logoImageUrlOld = shopOld.getImageUrl(); //獲取店鋪原Logo路徑
count = shopDao.updateShopLogo(shop); //上傳店鋪新的Logo
if(count>0){ //上傳成功
return logoImageUrlOld; //返回店鋪原Logo路徑
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
@CacheEvict(value="shopCache",allEntries=true)
@Override
public int updateShop(Shop shop,List<ShopMac> shopMacList) {
int count = 0;
try {
count = shopDao.updateShop(shop); //更新店鋪信息
shopDao.deleteShopMacByShopNum(shop.getShopNum()); //根據店鋪編號刪除原店鋪Mac
shopDao.addShopMac(shopMacList); //添加店鋪Mac
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
@Override
public void checkShop(Shop shop) {
shopDao.checkShop(shop);
}
@Override
public PageView query(PageView pageView, Shop shop) {
List<Shop> shopList = shopDao.query(pageView, shop);
pageView.setRecords(shopList);
return pageView;
}
@Override
public Shop getById(String id) {
return shopDao.getById(id);
}
@Override
public void checkShopBatchAgree(List<String> checkList) {
try {
shopDao.checkShopBatchAgree(checkList);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void checkShopBatchReject(List<String> checkList) {
try {
shopDao.checkShopBatchReject(checkList);
} catch (Exception e) {
e.printStackTrace();
}
}
@CacheEvict(value="shopCache",allEntries=true)
@Override
public int deletebyShopNum(String shopNum) {
int count = 0;
try {
count = shopDao.deletebyShopNum(shopNum);
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
@CacheEvict(value="shopCache",allEntries=true)
@Override
public int isRecommend(Shop shop) {
int count = 0;
try {
count = shopDao.isRecommend(shop);
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
@CacheEvict(value="shopCache",allEntries=true)
@Override
public void deleteBatch(String[] ids) {
try {
//將id數組轉爲list
List<String> idList = Arrays.asList(ids);
//將ids放入Map
Map<String,Object> idsMap = new HashMap<String,Object>();
idsMap.put("idList", idList);
//根據ids批量查詢 獲得shopNum
List<String> shopNums = shopDao.getShopNumsByids(idsMap);
shopDao.deleteBatch(ids); //根據店鋪Id批量刪除店鋪信息 t_shop表
shopDao.deleteShopMacByShopNums(shopNums); //根據店鋪shopNum批量刪除店鋪Mac信息 t_shop_mac表 根據shopNum刪除
} catch (Exception e) {
e.printStackTrace();
}
}
}
四,測試
第一次調用查詢的方法,發現sql被執行了
第二次調用查詢的方法,發現sql沒執行,但是數據依然存在,證明被緩存了。
第三次調用刪除的方法,清除緩存。
第四次調用查詢的方法,sql又被執行了,證明刪除方法帶動清除了緩存,保證數據同步。