也是在整合redis的時候偶然間發(fā)現(xiàn)spring-cache的模软。這也是一個不錯的框架伟骨,與spring的事務(wù)使用類似,只要添加一些注解方法燃异,就可以動態(tài)的去操作緩存了底靠,減少代碼的操作。如果這些注解不滿足項目的需求特铝,我們也可以參考spring-cache的實現(xiàn)思想,使用AOP代理+緩存操作來管理緩存的使用壹瘟。
在這個例子中我使用的是redis鲫剿,當然,因為spring-cache的存在稻轨,我們可以整合多樣的緩存技術(shù)灵莲,例如Ecache、Mamercache等殴俱。
下面來看springcache的具體操作吧政冻!
附上官方的文檔:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
redis中整合spring-cache
我代碼的工程還是接著上個項目來使用的, 所以可以結(jié)合上一篇博客來看
http://blog.csdn.net/u011521890/article/details/78070773
或者直接找github线欲,歡迎star
https://github.com/hpulzl/book_recommend
緩存的配置如下
- 在RedisCacheConfig上添加注解
@EnableCaching //加上這個注解是的支持緩存注解
- 創(chuàng)建RedisCacheManager
/**
* 設(shè)置RedisCacheManager
* 使用cache注解管理redis緩存
*
* @return
*/
@Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
return redisCacheManager;
}
- 自定義緩存的key
/**
* 自定義生成redis-key
*
* @return
*/
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName()).append(".");
sb.append(method.getName()).append(".");
for (Object obj : objects) {
sb.append(obj.toString());
}
System.out.println("keyGenerator=" + sb.toString());
return sb.toString();
}
};
}
在RedisCacheConfig中添加以上的代碼明场,就可以使用springcache的注解了。下面介紹springcache的注解如何使用
spring cache與redis緩存結(jié)合
對springCache概念的了解
springCache支持透明的添加緩存到應(yīng)用程序李丰,類似事務(wù)處理一般苦锨,不需要復雜的代碼支持。
緩存的主要使用方式包括以下兩方面
- 緩存的聲明,需要根據(jù)項目需求來妥善的應(yīng)用緩存
- 緩存的配置方式舟舒,選擇需要的緩存支持拉庶,例如Ecache、redis秃励、memercache等
緩存的注解介紹
@Cacheable 觸發(fā)緩存入口
@CacheEvict 觸發(fā)移除緩存
@CacahePut 更新緩存
@Caching 將多種緩存操作分組
@CacheConfig 類級別的緩存注解氏仗,允許共享緩存名稱
@CacheConfig
該注解是可以將緩存分類,它是類級別的注解方式夺鲜。我們可以這么使用它皆尔。
這樣的話,UseCacheRedisService的所有緩存注解例如@Cacheable的value值就都為user谣旁。
@CacheConfig(cacheNames = "user")
@Service
public class UseCacheRedisService {}
在redis的緩存中顯示如下
127.0.0.1:6379> keys *
1) "user~keys"
2) "user_1"
127.0.0.1:6379> get user~keys
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type user~keys
zset
127.0.0.1:6379> zrange user~keys 0 10
1) "user_1"
我們注意到床佳,生成的user~keys,它是一個zset類型的key榄审,如果使用get會報WRONGTYPE Operation against a key holding the wrong kind of value砌们。這個問題坑了我很久
@Cacheable
一般用于查詢操作,根據(jù)key查詢緩存.
- 如果key不存在搁进,查詢db浪感,并將結(jié)果更新到緩存中。
- 如果key存在饼问,直接查詢緩存中的數(shù)據(jù)影兽。
查詢的例子,當?shù)谝徊樵兊臅r候莱革,redis中不存在key峻堰,會從db中查詢數(shù)據(jù),并將返回的結(jié)果插入到redis中盅视。
@Cacheable
public List<User> selectAllUser(){
return userMapper.selectAll();
}
調(diào)用方式捐名。
@Test
public void selectTest(){
System.out.println("===========第一次調(diào)用=======");
List<User> list = useCacheRedisService.selectAllUser();
System.out.println("===========第二次調(diào)用=======");
List<User> list2 = useCacheRedisService.selectAllUser();
for (User u : list2){
System.out.println("u = " + u);
}
}
打印結(jié)果,大家也可以試一試
只輸出一次sql查詢的語句闹击,說明第二次查詢是從redis中查到的镶蹋。
===========第一次調(diào)用=======
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
DEBUG [main] - ==> Preparing: SELECT id,name,sex,age,password,account FROM user
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
===========第二次調(diào)用=======
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
u = User{id=1, name='fsdfds', sex='fdsfg', age=24, password='gfdsg', account='gfds'}
redis中的結(jié)果
我們可以看到redis中已經(jīng)存在
com.lzl.redisService.UseCacheRedisService.selectAllUser.記錄了。
這么長的一串字符key是根據(jù)自定義key值生成的赏半。
127.0.0.1:6379> keys *
1) "user~keys"
2) "com.lzl.redisService.UseCacheRedisService.selectAllUser."
3) "user_1"
127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.selectAllUser.
"[\"java.util.ArrayList\",[[\"com.lzl.bean.User\",{\"id\":1,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]]]"
@CachePut
一般用于更新和插入操作贺归,每次都會請求db
通過key去redis中進行操作。
- 如果key存在断箫,更新內(nèi)容
- 如果key不存在拂酣,插入內(nèi)容。
/**
* 單個user對象的插入操作仲义,使用user+id
* @param user
* @return
*/
@CachePut(key = "\"user_\" + #user.id")
public User saveUser(User user){
userMapper.insert(user);
return user;
}
redis中的結(jié)果
多了一條記錄user_2
127.0.0.1:6379> keys *
1) "user~keys"
2) "user_2"
3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."
4) "user_1"
127.0.0.1:6379> get user_2
"[\"com.lzl.bean.User\",{\"id\":2,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]"
@CacheEvict
根據(jù)key刪除緩存中的數(shù)據(jù)踱葛。allEntries=true表示刪除緩存中的所有數(shù)據(jù)丹莲。
@CacheEvict(key = "\"user_\" + #id")
public void deleteById(Integer id){
userMapper.deleteByPrimaryKey(id);
}
測試方法
@Test
public void deleteTest(){
useCacheRedisService.deleteById(1);
}
redis中的結(jié)果
user_1已經(jīng)移除掉。
127.0.0.1:6379> keys *
1) "user~keys"
2) "user_2"
3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."
測試allEntries=true時的情形尸诽。
@Test
public void deleteAllTest(){
useCacheRedisService.deleteAll();
}
@CacheEvict(allEntries = true)
public void deleteAll(){
userMapper.deleteAll();
}
redis中的結(jié)果
redis中的數(shù)據(jù)已經(jīng)全部清空
127.0.0.1:6379> keys *
(empty list or set)
@Caching
通過注解的屬性值可以看出來甥材,這個注解將其他注解方式融合在一起了,我們可以根據(jù)需求來自定義注解性含,并將前面三個注解應(yīng)用在一起
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
使用例子如下
@Caching(
put = {
@CachePut(value = "user", key = "\"user_\" + #user.id"),
@CachePut(value = "user", key = "#user.name"),
@CachePut(value = "user", key = "#user.account")
}
)
public User saveUserByCaching(User user){
userMapper.insert(user);
return user;
}
@Test
public void saveUserByCachingTest(){
User user = new User();
user.setName("dkjd");
user.setAccount("dsjkf");
useCacheRedisService.saveUserByCaching(user);
}
redis中的執(zhí)行結(jié)果
一次添加三個key
127.0.0.1:6379> keys *
1) "user~keys"
2) "dsjkf"
3) "dkjd"
4) "user_3"
結(jié)合@Caching還可以設(shè)置自定義的注解
自定義緩存注解
@Caching(
put = {
@CachePut(value = "user", key = "\"user_\" + #user.id"),
@CachePut(value = "user", key = "#user.name"),
@CachePut(value = "user", key = "#user.account")
}
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SaveUserInfo {
}
使用如下
@SaveUserInfo
public User saveUserByInfo(User user){
userMapper.insert(user);
return user;
}
測試
@Test
public void saveUserByInfoTest(){
User user = new User();
user.setName("haha");
user.setAccount("hhhcc");
useCacheRedisService.saveUserByInfo(user);
}
redis結(jié)果
127.0.0.1:6379> keys *
1) "user_4"
2) "dsjkf"
3) "dkjd"
4) "user~keys"
5) "haha"
6) "hhhcc"
7) "user_3"
通過以上的例子基本可以了解springcache的使用了洲赵,當然還有更加復雜的操作,這里只是簡單的介紹一下商蕴,運用到實際的項目中還是有所欠缺的叠萍。不過有這個基礎(chǔ)應(yīng)該不會太難。同時有時間可以再研究一下spring-cache的實現(xiàn)原理绪商。是基于AOP的實現(xiàn)的苛谷,這也是我們在項目中學習的地方。
遇到的兩個問題
WRONGTYPE Operation against a key holding the wrong kind of value
這個就是上面所說的類型不一致格郁,使用redis命令不當造成的腹殿。所以在查找redis的value時候,需要知道key的類型例书。
type key
Invalid argument(s)
還是redis現(xiàn)實的錯誤锣尉,這個有些困惑,在get的時候决采,一定要加上""(引號)才行自沧。
127.0.0.1:6379> keys *
1) "user_4"
2) "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}"
3) "dsjkf"
4) "dkjd"
5) "user~keys"
6) "haha"
7) "hhhcc"
8) "user_3"
127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}
Invalid argument(s)
127.0.0.1:6379> type com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}
Invalid argument(s)
127.0.0.1:6379> get "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}"
"[\"com.lzl.bean.User\",{\"id\":5,\"name\":\"fsdsg\",\"sex\":\"vcxvx\",\"age\":24,\"password\":\"vcxvcxc\",\"account\":\"vxcvxc\"}]"