Redis两个单词之间用冒号":"连接,就像数据库中用下划线连接,Java中用驼峰式。
list:命令用l、r开头
Set:命令用s开头
Hash:命令用h开头
Zset(有序集合):命令用z开头
zadd test 10 aaa 20 bbb 30 ccc:设置有序集合test。
zscore test ccc:查询test中ccc的分数,返回30。
# 有序集合
127.0.0.1:6379> zadd test 10 aaa 20 bbb 30 ccc 40 ddd 50 eee
(integer) 5
127.0.0.1:6379> zcard test # 集合元素个数
(integer) 5
127.0.0.1:6379> zscore test ccc # ccc的分数
"30"
127.0.0.1:6379> zrank test ccc # 按分数从小到大排序,从0开始,ccc排在2
(integer) 2
127.0.0.1:6379> zrange test 0 2 # 打印从0到2的内容
1) "aaa"
2) "bbb"
3) "ccc"
# 有16个库
keys * # 查看这个库里的所有key
keys test* #查看这个库里所有以test开头的数据
127.0.0.1:6379> type test # 查看test这个key的类型
zset
127.0.0.1:6379> exists test # 查看test这个key是否存在,1表示存在,0表示不存在
(integer) 1
127.0.0.1:6379> expire test 10 # 10秒后删除test
(integer) 1
127.0.0.1:6379> del test # del可以删除任何类型的key
(integer) 1
当你定义的一个Bean(上面有@Bean注释),需要某个被Spring接管的对象,你可以直接在Bean的参数里写它,Spring就会把这个对象自动给你注入进来。
Redis事务不满足ACID,它的机制是:你启动事务,再去执行Redis命令,它不会立刻执行命令,它会把你后面传入的命令都放到队列里,等你提交事务时,它会把你的命令一股脑发给Redis服务器,一起执行。所以不要在Redis事务的中间做查询,不然会查出来的是空数据。
package com.nowcoder.community;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class RedisTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testStrings() {
String redisKey = "test:count";
redisTemplate.opsForValue().set(redisKey, 1);
System.out.println(redisTemplate.opsForValue().get(redisKey));
System.out.println(redisTemplate.opsForValue().increment(redisKey));
System.out.println(redisTemplate.opsForValue().decrement(redisKey));
}
@Test
public void testHashes() {
String redisKey = "test:user";
redisTemplate.opsForHash().put(redisKey, "id", 1);
redisTemplate.opsForHash().put(redisKey, "username", "zhangsan");
System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
System.out.println(redisTemplate.opsForHash().get(redisKey, "username"));
}
@Test
public void testLists() {
String redisKey = "test:ids";
redisTemplate.opsForList().leftPush(redisKey, 101);
redisTemplate.opsForList().leftPush(redisKey, 102);
redisTemplate.opsForList().leftPush(redisKey, 103);
System.out.println(redisTemplate.opsForList().size(redisKey));
System.out.println(redisTemplate.opsForList().index(redisKey, 0));
System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
System.out.println(redisTemplate.opsForList().leftPop(redisKey));
}
@Test
public void testSets() {
String redisKey = "test:teachers";
redisTemplate.opsForSet().add(redisKey, "刘备", "关羽", "张飞", "赵云", "诸葛亮");
System.out.println(redisTemplate.opsForSet().size(redisKey));
System.out.println(redisTemplate.opsForSet().pop(redisKey));
System.out.println(redisTemplate.opsForSet().members(redisKey));
}
@Test
public void testSortedSets() {
String redisKey = "test:students";
redisTemplate.opsForZSet().add(redisKey, "唐僧", 80);
redisTemplate.opsForZSet().add(redisKey, "悟空", 90);
redisTemplate.opsForZSet().add(redisKey, "八戒", 50);
redisTemplate.opsForZSet().add(redisKey, "沙僧", 70);
redisTemplate.opsForZSet().add(redisKey, "白龙马", 60);
System.out.println(redisTemplate.opsForZSet().zCard(redisKey));
System.out.println(redisTemplate.opsForZSet().score(redisKey, "八戒"));
System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey, "八戒"));
System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey, 0, 2));
}
@Test
public void testKeys() {
redisTemplate.delete("test:user");
System.out.println(redisTemplate.hasKey("test:user"));
redisTemplate.expire("test:students", 10, TimeUnit.SECONDS);
}
// 多次访问同一个key。
// 简化要调用的方法,比如redisTemplate.opsForValue().increment(redisKey)->operations.increment();
@Test
public void testBoundOperations() {
String redisKey = "test:count";
BoundValueOperations operations = redisTemplate.boundValueOps(redisKey);
operations.increment();
operations.increment();
operations.increment();
operations.increment();
operations.increment();
System.out.println(operations.get());
}
// 编程式事务
// Redis不满足ACID原则,Redis会把操作放队列里,完成后统一提交。因此,不能把查询任务放到事务里,不然会查出来的是空数据。
@Test
public void testTransactional() {
Object obj = redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String redisKey = "test:tx";
operations.multi();//启动事务
operations.opsForSet().add(redisKey, "zhangsan");
operations.opsForSet().add(redisKey, "lisi");
operations.opsForSet().add(redisKey, "wangwu");
System.out.println(operations.opsForSet().members(redisKey));
return operations.exec();//提交事务
}
});
System.out.println(obj);
}
}
异步请求,就是不刷新整个页面,在后台返回后,页面局部刷新,像ajax(jQuery的一种)那样。异步请求的后台的Controller要加@ResponseBody,不是跳转到某个页面,而是返回给页面某个字符串,页面显示出这个字符串。
貌似加@ResponseBody的都是post请求(@RequestMapping(path = “/letter/send”, method = RequestMethod.POST)),没加的都是get请求。
开发用户主页,显示用户收到了多少个赞。
zset用关注时间作为分数,可以把关注的用户按照关注的时间顺序列举出来。RedisKeyUtil类:
// 某个实体拥有的粉丝
// follower:entityType:entityId -> zset(userId,now)
public static String getFollowerKey(int entityType, int entityId) {
return PREFIX_FOLLOWER + SPLIT + entityType + SPLIT + entityId;
}
FollowService类的public List
课后作业(第四章最下面有给答案):
开发我的帖子功能,即在个人主页上,显示当前用户曾经发布过的帖子。
开发我的回复功能,即在个人主页上,显示当前用户曾经为帖子发布过的评论。
RedisKeyUtil类的private static final String PREFIX_KAPTCHA = “kaptcha”;//验证码的英文是captcha。但生成验证码的jar包是Kaptcha。
LoginController类的
@RequestMapping(path = “/kaptcha”, method = RequestMethod.GET)
public void getKaptcha(HttpServletResponse response/, HttpSession session/)方法:
// 验证码的归属
String kaptchaOwner = CommunityUtil.generateUUID();
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60);//cookie的有效时间:1分钟
cookie.setPath(contextPath);//cookie的有效路径:整个项目下都有效
response.addCookie(cookie);//把cookie发送给客户端
// 将验证码存入Redis
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);//得到key
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);//60秒生效时间
LoginController类的方法
@RequestMapping(path = "/login", method = RequestMethod.POST)
public String login(String username, String password, String code, boolean rememberme,
Model model, /*HttpSession session, */HttpServletResponse response,
@CookieValue("kaptchaOwner") String kaptchaOwner) {//接受变量名为kaptchaOwner的cookie
UserService类(Redis的value可以存取对象):
//UserService类的public Map login(String username, String password, int expiredSeconds)方法:
redisTemplate.opsForValue().set(redisKey, loginTicket);//Redis会把loginTicket这个对象序列化为JSON字符串来保存
//UserService类的public void logout(String ticket)方法
LoginTicket loginTicket = (LoginTicket)redisTemplate.opsForValue().get(redisKey);//返回的是个Object对象,强转为LoginTicket对象
使用Redis存储登录凭证:cookie里的ticket和LoginTicket loginTicket里的ticket都是CommunityUtil.generateUUID()。而把LoginTicket loginTicket存到Redis里的key是String redisKey = RedisKeyUtil.getTicketKey(ticket);,即ticket:CommunityUtil.generateUUID()。
LoginController类里的登录/退出登录的方法存取cookie里的ticket。UserService类里的登录/退出登录的方法,用由cookie里的ticket拼接出的redisKey作为key,来存取Redis里的loginTicket。
UserService类:
public int updateHeader(int userId, String headerUrl) {
// return userMapper.updateHeader(userId, headerUrl);
int rows = userMapper.updateHeader(userId, headerUrl);//更新数据库和清理缓存。先更新数据库,后清理缓存,因为如果更新数据库失败了,就不用清理缓存了
clearCache(userId);
return rows;
}