公司项目有些地方需要用到缓存,考虑到需要缓存的东西不多,还没到使用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 支持如下几个参数:
//清除掉指定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>
<?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又被执行了,证明删除方法带动清除了缓存,保证数据同步。