昨天为止,差不多是把redis缓存应用方面学习完毕,有了初步的概念,虽然原理部分还不是很懂,但是至少已经可以用了,现在进行总结:
1.配置部分:
配置部分不难,主要是一些属性,备注上也写了。
2:初步使用
@SpringBootTest public class DemoApplicationTests { @Autowired RedisTemplate<String,String> redisTemplate; @Test public void testString(){ //操作String类型的数据 ValueOperations<String, String> valueStr = redisTemplate.opsForValue(); //spring RedisTemplate中封装了很多的操作器,
如 HashOperations,ZSetOperations等,分别能操作hash类型的数据结
构和有序的Set类型数据结构,使用这些操作器能方便的对Map结构和Set结构数据进程操作。 //存储一条数据 valueStr.set("goodsProdu","长安"); //获取一条数据并输出 String goodsName = valueStr.get("goodsProdu"); System.out.println(goodsName); //存储多条数据 Map<String,String> map = new HashMap<>(); map.put("goodsName","福特汽车"); map.put("goodsPrice","88888"); map.put("goodsId","88"); valueStr.multiSet(map); //获取多条数据 System.out.println("========================================"); List<String> list = new ArrayList<>(); list.add("goodsName"); list.add("goodsPrice"); list.add("goodsId"); list.add("goodsProdu"); List<String> listKeys = valueStr.multiGet(list); for (String key : listKeys) { System.out.println(key); } }
3:进阶使用:数据库加上redis缓存,如果缓存中没有,再去数据库中查找
server: port: 8081 tomcat: uri-encoding: UTF-8 spring: http: encoding: charset: UTF-8 force: true enabled: true datasource: driver-class-name: com.mysql.cj.jdbc.Driver # mysql数据库位置 url: jdbc:mysql://192.168.13.130:3306/ssm_sms?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: true redis: # Redis数据库索引(默认为0) database: 0 # Redis服务器地址 host: 192.168.13.130 # Redis服务器连接端口 port: 6379 # Redis服务器连接密码(默认为空) password: # 连接超时时间(毫秒) timeout: 1000 jedis.pool: # 连接池最大连接数(使用负值表示没有限制) max-active: 8 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1 # 连接池中的最大空闲连接 max-idle: 8 # 连接池中的最小空闲连接 min-idle: 0
@Controller public class GetUser { @Autowired UserMapper userMapper; @Autowired private RedisTemplate redisTemplate; @RequestMapping("/getUsers") @ResponseBody String getUsers(){ long startTime; long endTime; startTime = System.currentTimeMillis(); //获取redis中key="userJSON"的value String userJSON = (String)redisTemplate.opsForValue().get("userJSON"); if(userJSON!=null){ endTime = System.currentTimeMillis(); System.out.println("取缓存成功,耗时" + (endTime-startTime) + "ms"); return userJSON; } //在redis中获取不到则执行以下代码 startTime = System.currentTimeMillis(); List<User> userList = userMapper.getUserList(); //将userList转为json字符串 userJSON = JSONArray.fromObject(userList).toString(); endTime = System.currentTimeMillis(); //将查询结果存入redis中作缓存 //四个参数从左至右分别为:key,value,有效时间,时间单位 //这里即:创建一个key="userJSON",value=#{userJSON}的键值对,其有效时间是10秒 redisTemplate.opsForValue().set("userJSON",userJSON,10, TimeUnit.SECONDS); System.out.println("取缓存失败,耗时" + (endTime-startTime) + "ms"); return userJSON; } @RequestMapping("/getUsers2") @ResponseBody public ModelAndView getUsers2(){ ModelAndView modelAndView =new ModelAndView(); long listLength = redisTemplate.opsForList().size("userJSON"); List<User> list = redisTemplate.opsForList().range("userJSON",0,listLength); if(list.size()!=0){ List<User> userList = userMapper.getUserList(); System.out.println("取缓存成功,耗时"); modelAndView.setViewName("wujifu"); modelAndView.addObject("list",userList); return modelAndView; } //在redis中获取不到则执行以下代码 List<User> userList = userMapper.getUserList(); for(User user : userList){ redisTemplate.opsForList().leftPush("userJSON",user); } modelAndView.setViewName("wujifu"); modelAndView.addObject("list",userList); return modelAndView; } }
这里第一个方法是将缓存以jason的方式存入redis 取出来的时候转为字符串,但是我觉得太麻烦了,而且实际开发肯定不这么麻烦,所以我想直接用list的方式存进去,然后取出作为list输出在页面上,然后发现其实redis自己是支持很多类型的存取的:
String类型:
@Test public void testString(){ //设置键 获取键 get set jedis.set("steve","stevetao"); System.out.println("设置后值:"+jedis.get("steve")); //追加键 append jedis.append("steve"," Is Good Man"); System.out.println("追加后值:"+jedis.get("steve")); //删除操作 del jedis.del("steve"); System.out.println("删除后值:"+jedis.get("steve")); //不存在就保存, setnx msetnx jedis.setnx("steve","stevetao"); System.out.println("设置后值:"+jedis.get("steve")); System.out.println("再次设置后值:"+jedis.setnx("steve","stevetao")); //截取字符串 substr System.out.println("截取后值:"+jedis.substr("steve",0,4)); //设置多个键值对 mset mget jedis.mset(new String[]{"zhangsan","123","lisi","1234"}); System.out.println("多次设置后值:"+jedis.mget("zhangsan","lisi")); //递增递减 incr decr incrby decrby jedis.incr("zhangsan"); jedis.decr("lisi"); System.out.println("递增递减后值:"+jedis.mget("zhangsan","lisi")); jedis.incrBy("zhangsan",6); jedis.decrBy("lisi",3); System.out.println("递增递减后值:"+jedis.mget("zhangsan","lisi")); }
List类型:
@Test public void testList(){ //尾添加 rpush 头添加 lpush jedis.lpush("books","java","C++","Ruby","Scala","python"); jedis.rpush("language","java","C++","Ruby","Scala","python"); // -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 System.out.println("头添加后books值:"+jedis.lrange("books",0,-1)); System.out.println("尾添加后language值:"+jedis.lrange("language",0,-1)); //尾部删除 rpop 头部删除 lpop System.out.println("删除的值为:"+jedis.lpop("books")); System.out.println("删除的值为:"+jedis.rpop("language")); System.out.println("头部删除后books值:"+jedis.lrange("books",0,-1)); System.out.println("尾部删除后language值:"+jedis.lrange("language",0,-1)); //尾部删除并头添加 rpoplpush jedis.rpoplpush("language","books"); System.out.println("尾部删除并头添加后books值:"+jedis.lrange("books",0,-1)); System.out.println("尾部删除并头添加后language值:"+jedis.lrange("language",0,-1)); //区别: 只能给存在的list做添加,不能项lpush那样能新增list jedis.lpushx("books","php"); jedis.lpushx("book","php"); System.out.println("头添加后books值:"+jedis.lrange("books",0,-1)); System.out.println("头添加后book值:"+jedis.lrange("book",0,-1)); //获取集合长度 llen 指定索引的值 lindex 保留截取的值 ltrim System.out.println("books集合长度:"+jedis.llen("books")); System.out.println("books集合第二个数值:"+jedis.lindex("books",1)); jedis.ltrim("books",0,2); System.out.println("截取后books值:"+jedis.lrange("books",0,-1)); }
Hash类型
@Test public void testHash(){ //适合字段:设值hset 取值hget(如果value是json字符串,类似保存对象) jedis.hset("student","name","zhangsan"); System.out.println("student中name的值为:"+jedis.hget("student","name")); //适合对象:设值hmset 取值hmget Map<String,String> map = new HashMap<String,String>(); map.put("name", "lisi"); map.put("age", "36"); jedis.hmset("teacher",map); System.out.println("teacher中name、age的值为:"+jedis.hmget("teacher","name","age")); //teacher是否存在键age hexists if(jedis.hexists("teacher","age")){ //给指定值增加4 hincrBy jedis.hincrBy("teacher","age",4); System.out.println("teacher中name、age的值为:"+jedis.hmget("teacher","name","age")); } //返回key的个数hlen 返回值hvals 返回键hkeys 键值对hgetAll jedis.hset("student","age","13"); jedis.hset("student","qq","2246920330"); jedis.hset("student","address","beijing"); System.out.println("student中键的个数为:"+jedis.hlen("student")); System.out.println("student中所有的键为:"+jedis.hkeys("student")); System.out.println("student中所有的值为:"+jedis.hvals("student")); System.out.println("student中所有的键值对为:"+jedis.hgetAll("student")); //删除 hdel jedis.hdel("student",new String[]{"address","qq","age"}); System.out.println("删除后,student中所有的键值对为:"+jedis.hgetAll("student")); }
Set类型
@Test public void testSet(){ //set中添加值 sadd 取值 smembers jedis.sadd("student","Jan","John","Steve","jack","lili","peter","Anna"); jedis.sadd("girls","Jan","lili","Alice","Jeanne","Anna"); System.out.println("排名不分先后:"+jedis.smembers("student")); //set个数 scard 是否存在某个值 sismember System.out.println("set集合的个数:"+jedis.scard("student")); System.out.println("student是否存在steve:"+jedis.sismember("student","Steve")); System.out.println("student是否存在stevetao:"+jedis.sismember("student","Stevetao")); //System.out.println(jedis.sscan("student","0").getResult()); //删除指定的值 srem 随机删除并返回 spop System.out.println("删除指定的值Steve:"+jedis.srem("student","Steve")); System.out.println("删除的值为:"+jedis.spop("student")); System.out.println("再次排名不分先后:"+jedis.smembers("student")); //集合操作 System.out.println("两个set的交集:"+jedis.sinter("student","girls")); System.out.println("两个set的并集:"+jedis.sunion("student","girls")); System.out.println("student对girls的差集:"+jedis.sdiff("student","girls")); System.out.println("girls对student的差集:"+jedis.sdiff("girls","student")); //集合操作并保存 jedis.sinterstore("jiaoji","student","girls"); jedis.sunionstore("bingji","student","girls"); jedis.sdiffstore("chaji","student","girls"); System.out.println("交集:"+jedis.smembers("jiaoji")); System.out.println("并集:"+jedis.smembers("bingji")); System.out.println("student对girls的差集:"+jedis.smembers("chaji"));
ZSet类型
@Test public void testZset(){ jedis.zadd("math",75,"Jim"); jedis.zadd("math",86,"Lina"); jedis.zadd("math",52,"Dive"); jedis.zadd("math",91,"Bobber"); System.out.println("有序集合的成员数:"+jedis.zcard("math")); System.out.println("有序集合的成员:"+jedis.zrevrangeByScore("math",100,0)); //返回set<Tuple> System.out.println("有序集合的成员:"+jedis.zrangeWithScores("math",0,100)); }
具体操作也可以看我例子;
还有关于redistemplate的意义,就是原本的模板是<String,String>类型的,改成用<String ,Object>来存储
https://www.cnblogs.com/songanwei/p/9274348.html
https://www.cnblogs.com/superfj/p/9232482.html
这两个网址中的例子都非常棒!
https://zhuanlan.zhihu.com/p/52631249
这是关于template的详解
其实不重写的话也没事,主要是序列化的问题啦,还有工具类,网上很多,就是将redistemplate的功能封装一下,使得操作更加简便
还有注解方式的使用
就两点
1.主函数那里要加开启注解的注解
2.
注释介绍
- @Cacheable
- @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
- @Cacheable 作用和配置方法
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @Cacheable(value=”mycache”) @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @Cacheable(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
@CachePut
- @CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
- @CachePut 作用和配置方法
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | @CachePut(value=”my cache”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @CachePut(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @CachePut(value=”testcache”,condition=”#userName.length()>2”) |
@CacheEvict
- @CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
- @CacheEvict 作用和配置方法
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | @CacheEvict(value=”my cache”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @CacheEvict(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @CacheEvict(value=”testcache”,condition=”#userName.length()>2”) |
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 | @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | @CachEvict(value=”testcache”,beforeInvocation=true) |
@CacheConfig
所有的@Cacheable()里面都有一个value=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了, 所以,有了@CacheConfig这个配置,一个类中可能会有多个缓存操作,而这些缓存操作可能是重复的。这个时候可以使用@CacheConfig。
- @CacheConfig("books")
- public class BookRepositoryImpl implements BookRepository {
- @Cacheable
- public Book findBook(ISBN isbn) {...}
- }
//@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存; @Cacheable(value = "user", key = "#id", condition = "#id lt 10") public User conditionFindById(final Long id) //@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存; @CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'") public User conditionSave(final User user) //@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反) @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'") public User conditionSave2(final User user) //@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存; @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'") public User conditionDelete(final User user)
@Caching
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id–>user;username—>user;email—>user的缓存;此时就需要@Caching组合多个注解标签了。
@Caching(put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.email") }) public User save(User user) { }
好了 redis缓存的基本使用到此结束!