Springboot中Spring-cache与redis整合
来源:互联网 发布:js 数组对象合并 编辑:程序博客网 时间:2024/06/05 10:54
也是在整合redis的时候偶然间发现spring-cache的。这也是一个不错的框架,与spring的事务使用类似,只要添加一些注解方法,就可以动态的去操作缓存了,减少代码的操作。如果这些注解不满足项目的需求,我们也可以参考spring-cache的实现思想,使用AOP代理+缓存操作来管理缓存的使用。
在这个例子中我使用的是redis,当然,因为spring-cache的存在,我们可以整合多样的缓存技术,例如Ecache、Mamercache等。
下面来看springcache的具体操作吧!
附上官方的文档:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
redis中整合spring-cache
我代码的工程还是接着上个项目来使用的, 所以可以结合上一篇博客来看
http://blog.csdn.net/u011521890/article/details/78070773
或者直接找github,欢迎star
https://github.com/hpulzl/book_recommend
缓存的配置如下
- 在RedisCacheConfig上添加注解
@EnableCaching //加上这个注解是的支持缓存注解
- 创建RedisCacheManager
/** * 设置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缓存结合
对springCache概念的了解
springCache支持透明的添加缓存到应用程序,类似事务处理一般,不需要复杂的代码支持。
缓存的主要使用方式包括以下两方面
1. 缓存的声明,需要根据项目需求来妥善的应用缓存
2. 缓存的配置方式,选择需要的缓存支持,例如Ecache、redis、memercache等
缓存的注解介绍
@Cacheable 触发缓存入口
@CacheEvict 触发移除缓存
@CacahePut 更新缓存
@Caching 将多种缓存操作分组
@CacheConfig 类级别的缓存注解,允许共享缓存名称
@CacheConfig
该注解是可以将缓存分类,它是类级别的注解方式。我们可以这么使用它。
这样的话,UseCacheRedisService的所有缓存注解例如@Cacheable的value值就都为user。
@CacheConfig(cacheNames = "user")@Servicepublic 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 value127.0.0.1:6379> type user~keyszset127.0.0.1:6379> zrange user~keys 0 101) "user_1"
我们注意到,生成的user~keys,它是一个zset类型的key,如果使用get会报WRONGTYPE Operation against a key holding the wrong kind of value。这个问题坑了我很久
@Cacheable
一般用于查询操作,根据key查询缓存.
- 如果key不存在,查询db,并将结果更新到缓存中。
- 如果key存在,直接查询缓存中的数据。
查询的例子,当第一查询的时候,redis中不存在key,会从db中查询数据,并将返回的结果插入到redis中。
@Cacheable public List<User> selectAllUser(){ return userMapper.selectAll(); }
调用方式。
@Test public void selectTest(){ System.out.println("===========第一次调用======="); List<User> list = useCacheRedisService.selectAllUser(); System.out.println("===========第二次调用======="); List<User> list2 = useCacheRedisService.selectAllUser(); for (User u : list2){ System.out.println("u = " + u); } }
打印结果,大家也可以试一试
只输出一次sql查询的语句,说明第二次查询是从redis中查到的。
===========第一次调用=======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===========第二次调用=======keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.u = User{id=1, name='fsdfds', sex='fdsfg', age=24, password='gfdsg', account='gfds'}
redis中的结果
我们可以看到redis中已经存在
com.lzl.redisService.UseCacheRedisService.selectAllUser.记录了。
这么长的一串字符key是根据自定义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中进行操作。
1. 如果key存在,更新内容
2. 如果key不存在,插入内容。
/** * 单个user对象的插入操作,使用user+id * @param user * @return */ @CachePut(key = "\"user_\" + #user.id") public User saveUser(User user){ userMapper.insert(user); return user; }
redis中的结果
多了一条记录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
根据key删除缓存中的数据。allEntries=true表示删除缓存中的所有数据。
@CacheEvict(key = "\"user_\" + #id") public void deleteById(Integer id){ userMapper.deleteByPrimaryKey(id); }
测试方法
@Test public void deleteTest(){ useCacheRedisService.deleteById(1); }
redis中的结果
user_1已经移除掉。
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中的结果
redis中的数据已经全部清空
127.0.0.1:6379> keys *(empty list or set)
@Caching
通过注解的属性值可以看出来,这个注解将其他注解方式融合在一起了,我们可以根据需求来自定义注解,并将前面三个注解应用在一起
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中的执行结果
一次添加三个key
127.0.0.1:6379> keys *1) "user~keys"2) "dsjkf"3) "dkjd"4) "user_3"
结合@Caching还可以设置自定义的注解
自定义注解
@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@Documentedpublic @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结果
127.0.0.1:6379> keys *1) "user_4"2) "dsjkf"3) "dkjd"4) "user~keys"5) "haha"6) "hhhcc"7) "user_3"
通过以上的例子基本可以了解springcache的使用了,当然还有更加复杂的操作,这里只是简单的介绍一下,运用到实际的项目中还是有所欠缺的。不过有这个基础应该不会太难。同时有时间可以再研究一下spring-cache的实现原理。是基于AOP的实现的,这也是我们在项目中学习的地方。
遇到的两个问题
WRONGTYPE Operation against a key holding the wrong kind of value
这个就是上面所说的类型不一致,使用redis命令不当造成的。所以在查找redis的value时候,需要知道key的类型。
type key
Invalid argument(s)
还是redis现实的错误,这个有些困惑,在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\"}]"
- Springboot中Spring-cache与redis整合
- spring boot中spring cache 整合redis
- spring cache 与redis缓存整合
- redis与Spring Cache的整合
- 基spring cache 整合redis
- Redis的安装、部署和与Spring Cache整合
- 11.springboot整合默认spring cache
- java springboot与redis整合
- spring boot 中redis与cookie整合
- springBoot thymeleaf与Spring整合
- Redis与Spring整合
- redis与spring整合
- Redis与Spring整合
- Redis与Spring整合
- Spring与Redis整合
- Spring与Redis整合
- spring中整合redis
- redis与spring cache集成
- 如何快速、科学的配置GO语言编译Android环境
- Ubuntu 16.04如何禁用IPv6功能
- FYN 算法学习1~
- 如何轻松查看JAR包下的class源代码文件
- 归并排序
- Springboot中Spring-cache与redis整合
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛-I-GSM Base Station Identification(线性变换)
- 第四周项目三
- Android网络:发送http请求
- 【51Nod1765】谷歌的恐龙
- Mac系统之----教你怎么显示隐藏文件,或者关闭显示隐藏文件
- 怎么会有两个“原本”
- C++中链表的创建、输出、节点删除、节点插入、翻转、清空
- 深浅拷贝与写时拷贝的简单认知