四、Redis,一站式高性能存储方案
4.1、spring整合redis
-
导入依赖:spring-boot-starter-data-redis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置redis
- 配置数据库参数
# RedisProperties spring.redis.database=11 spring.redis.host=localhost spring.redis.port=6379
-
编写配置类,构造RedisTemplate
config.RedisConfig.java
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 设置key的序列化方式 template.setKeySerializer(RedisSerializer.string()); // 设置value的序列化方式 template.setValueSerializer(RedisSerializer.json()); // 设置hash的key的序列化方式 template.setHashKeySerializer(RedisSerializer.string()); // 设置hash的value的序列化方式 template.setHashValueSerializer(RedisSerializer.json()); template.afterPropertiesSet(); return template; } }
-
访问redis
- redisTemplate.opsForValue()
- redisTemplate.opsForHash()
- redisTemplate.opsForList()
- redisTemplate.opsForSet()
- redisTemplate.opsForZSet()
// 多次访问同一个key @Test public void testBoundOperations() { String redisKey = "test:count"; BoundValueOperations operations = redisTemplate.boundValueOps(redisKey); operations.increment(); System.out.println(operations.get()); }
// 编程式事务(用得多,事务中间查询没用 @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);//[1, 1, 1, [zhangsan, lisi, wangwu]] }
4.2、点赞
(1)service层
生成key的工具类RedisKeyUtil
直接写业务层,调用redisTemplate往redis中读取和存入数据即可,面向key编程,为了能够复用key,先创建一个可生成key的工具类: RedisKeyUtil,有些key是静态常量的,而有些key是动态的,由方法加入更明确的值并返回。
RedisKeyUtil.java:
实体的赞使用set集合,里面放入的是多个用户的id。set(userId), 既可以获取当前实体点赞数量,又可以获取点赞的用户,能够最大程度地满足各种各样需求的变化。
public class RedisKeyUtil {
private static final String SPLIT = ":";
private static final String PREFIX_ENTITY_LIKE = "like:entity";
private static final String PREFIX_USER_LIKE = "like:user";
// 某个实体的赞.entityType:被点赞的实体:帖子1、评论2、回复3;
// like:entity:entityType:entityId -> set(userId)
public static String getEntityLikeKey(int entityType, int entityId) {
return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;
}
}
LikeService
@Service
public class LikeService {
@Autowired
private RedisTemplate redisTemplate;
//点赞
//userID:谁点的赞;entityType:被点赞的实体:帖子1、评论2、回复3;
public void like(int userId, int entityType, int entityId){
//先拼好两个key
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
//判断某个用户是否对某个实体点过赞
boolean isMember = operations.opsForSet().isMember(entityLikeKey,userId);
if(isMember){
operations.opsForSet().remove(entityLikeKey, userId);
}
else{
operations.opsForSet().add(entityLikeKey, userId);
}
}
// 查询某实体被点赞的数量
public long findEntityLikeCount(int entityType, int entityId) {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
return redisTemplate.opsForSet().size(entityLikeKey);
}
// 查询某人对某实体的点赞状态
public int findEntityLikeStatus(int userId, int entityType, int entityId) {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
return redisTemplate.opsForSet().isMember(entityLikeKey, userId) ? 1 : 0;
}
}
(2)controller层
LikeController
异步请求:
@Controller
public class LikeController implements CommunityConstant {
@Autowired
private LikeService likeService;
@Autowired
private HostHolder hostHolder;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping(path="/like",method = RequestMethod.POST)
@ResponseBody
public String like(int entityType, int entityId){
//获取当前用户
User user = hostHolder.getUser();
//点赞
likeService.like(user.getId(), entityType, entityId);
//获取点赞总数量
long likeCount = likeService.findEntityLikeCount(entityType,entityId);
//目前是否被点赞的状态
int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
//返回的结果
Map<String, Object> map = new HashMap<>();
map.put("likeCount", likeCount);
map.put("likeStatus", likeStatus);
//返回结果
return CommunityUtil.getJSONString(0,null, map);
}
}
discuss-detail.html:
可以给三个地方点赞:给帖子点赞、给评论点赞、给回复点赞
HomeController
完善在首页显示帖子点赞的数量
@Autowired
private LikeService likeService;
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model, Page page) {
//.....
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());
map.put("likeCount", likeCount);
//.....
}
index.html
DiscussPostController
方法getDiscussPost获取帖子详情中
(1) 增加获取帖子点赞数量的处理代码
//点赞数量
long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST,discussPostId);
model.addAttribute("likeCount", likeCount);
//点赞状态(当前用户是否对帖子点过赞),如果用户未登录的话,也是显示未赞的状态
//likeStatus 为0,否则再去查询
int likeStatus = 0;
if(hostHolder.getUser() != null){
likeStatus = likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId);
(2) 对评论做相同的处理
likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT,comment.getId());
commentVo.put("likeCount", likeCount);
//点赞状态
likeStatus = 0;
if(hostHolder.getUser() != null){
likeStatus = likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId());
}
commentVo.put("likeStatus", likeStatus)
(3) 对回复做相同的处理
likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT,reply.getId());
replyVo.put("likeCount", likeCount);
//点赞状态
likeStatus = 0;
if(hostHolder.getUser() != null){
likeStatus = likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());
}
replyVo.put("likeStatus", likeStatus);
discuss-detail.html
4.3、我收到的赞
在用户点赞的是时候,增加一个维度。
RedisKeyUtil
//某个用户的赞
private static final String PREFIX_USER_LIKE = "like:user";
//like:user:userId -> int
public static String getUserLikeKey(int useId){
return PREFIX_USER_LIKE + SPLIT + useId;
}
LikeService(涉及事务)
涉及到了多个操作,使用事务,另外,查询要放到事务之外,如果放到事务之内,不会立马就返回查询结果。
//userID:谁点的赞;entityType:被点赞的实体:帖子1、评论2、回复3;entityID:实体id
//entityUserId:被赞的用户id
public void like(int userId, int entityType, int entityId, int entityUserId){
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String entityLikeKey =
RedisKeyUtil.getEntityLikeKey(entityType, entityId);
//被赞的用户id的key
String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId);
//判断某个用户是否对某个实体点过赞
boolean isMember =
operations.opsForSet().isMember(entityLikeKey,userId);
operations.multi();//开启事务:放在查询之后
if(isMember){//取消赞
operations.opsForSet().remove(entityLikeKey, userId);
operations.opsForValue().decrement(userLikeKey);
}else{//点赞
operations.opsForSet().add(entityLikeKey, userId);
if(operations.opsForValue().get(userLikeKey) == null){
operations.opsForValue().set(userLikeKey, 0);
}
operations.opsForValue().increment(userLikeKey);
}
return operations.exec();//表示的是提交事务
}
});
}
额外添加一个查询某个用户获得的赞的数量的方法:
//查询某个用户获得的赞
public int findUserLikeCount(int userId){
String userLikeKey = RedisKeyUtil.getUserLikeKey(userId);
Integer count = (Integer) redisTemplate.opsForValue().get(userLikeKey);
return count == null ? 0: count.intValue();
}
LikeController
需要额外添加 一个entityUserId输入参数:
@RequestMapping(path="/like",method = RequestMethod.POST)
@ResponseBody
public String like(int entityType, int entityId, int entityUserId){
//当前用户点赞
User user = hostHolder.getUser();
//点赞
likeService.like(user.getId(), entityType, entityId, entityUserId);
//数量
long likeCount = likeService.findEntityLikeCount(entityType,entityId);
//状态
int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
//返回的结果
Map<String, Object> map = new HashMap<>();
map.put("likeCount", likeCount);
map.put("likeStatus", likeStatus);
return CommunityUtil.getJSONString(0,null, map);
}
discuss-detail.html 传入entityUserId
UserController
显示个人主页时需要把用户收到的赞的总数量放到Model之中去。
个人主页不仅仅可以显示当前用户的主页,任意用户的主页也可以查看,因此路径中传入userID
@Autowired
private LikeService likeService;
@RequestMapping(path="/profile/{userId}", method = RequestMethod.GET)
public String getProfilePage(@PathVariable("userId") int userId, Model model){
User user = userService.findUserById(userId);
if(user == null){
throw new RuntimeException("该用户不存在");
}
//用户
model.addAttribute("user", user);
//点赞数量
int likeCount = likeService.findUserLikeCount(userId);
model.addAttribute("likeCount", likeCount);
return "/site/profile";
}
index.html 添加个人主页的路径
profile.html 个人主页
4.4、关注、取消关注
关键:设计好key!!!
RedisKeyUtil
用 zset,按照时间来统计,进行排序
public class RedisKeyUtil {
private static final String PREFIX_FOLLOWEE = "followee";//某个userID的关注
private static final String PREFIX_FOLLOWER = "follower";//某个uerID的粉丝
// entityType:被关注的实体:用户1、帖子2、题目3...等;
// 某个用户关注的实体
// followee:userId:entityType -> zset(entityId,now) 有序,以时间为顺序
public static String getFolloweeKey(int userId, int entityType) {
return PREFIX_FOLLOWEE + SPLIT + userId + SPLIT + entityType;
}
// 某个实体拥有的粉丝
// follower:entityType:entityId -> zset(userId,now)
public static String getFollowerKey(int entityType, int entityId) {
return PREFIX_FOLLOWER + SPLIT + entityType + SPLIT + entityId;
}
}
FollowService
事务管理:当前用户关注了实体的同时,被关注的实体的用户的粉丝增加
@Service
public class FollowService implements CommunityConstant {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UserService userService;
//==================关注=======================
public void follow(int userId, int entityType, int entityId){
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
//用户的关注集合
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
//实体的粉丝集合
String followerKey =
RedisKeyUtil.getFollowerKey(entityType, entityId);
redisOperations.multi();
//opsForZSet:有序集合
redisOperations.opsForZSet().add(
followeeKey, entityId, System.currentTimeMillis());
redisOperations.opsForZSet().add(
followerKey, userId, System.currentTimeMillis());
return redisOperations.exec();
}
});
}
//=======================取关======================
public void unfollow(int userId, int entityType, int entityId){
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
//用户的关注集合
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
//实体的粉丝集合
String followerKey =
RedisKeyUtil.getFollowerKey(entityType, entityId);
redisOperations.multi(); //开启事务
redisOperations.opsForZSet().remove(followeeKey, entityId);
redisOperations.opsForZSet().remove(followerKey, userId);
return redisOperations.exec(); //提交事务
}
});
}
//查询关注的实体的数量 followee:userId:entityType -> zset(entityId,now)
public long findFolloweeCount(int userId, int entityType){
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
return redisTemplate.opsForZSet().zCard(followeeKey);
}
//查询实体的粉丝的数量 follower:entityType:entityId -> zset(userId,now)
public long findFollowerCount(int entityType, int entityId){
String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
return redisTemplate.opsForZSet().zCard(followerKey);
}
//查询当前用户是否已关注该实体
public boolean hasFollowed(int userId, int entityType, int entityId){
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
return redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
}
}
FollowController(异步请求)
@Controller
public class FollowController implements CommunityConstant {
@Autowired
private FollowService followService;
@Autowired
private HostHolder hostHolder;
@Autowired
private UserService userService;
//关注
@RequestMapping(path = "/follow", method = RequestMethod.POST)
@ResponseBody
public String follow(int entityType, int entityId) {
User user = hostHolder.getUser();
followService.follow(user.getId(), entityType, entityId);
return CommunityUtil.getJSONString(0, "已关注!");
}
//取消关注
@RequestMapping(path = "/unfollow", method = RequestMethod.POST)
@ResponseBody
public String unfollow(int entityType, int entityId) {
User user = hostHolder.getUser();
followService.unfollow(user.getId(), entityType, entityId);
return CommunityUtil.getJSONString(0, "已取消关注!");
}
}
UserController
UserController中getProfilePage方法中补充一些代码逻辑
// 个人主页
@RequestMapping(path = "/profile/{userId}", method = RequestMethod.GET)
public String getProfilePage(@PathVariable("userId") int userId, Model model) {
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
// 用户
model.addAttribute("user", user);
// 点赞数量
int likeCount = likeService.findUserLikeCount(userId);
model.addAttribute("likeCount", likeCount);
//============================新增===================================
// 关注数量
long followeeCount =
followService.findFolloweeCount(userId, ENTITY_TYPE_USER);
model.addAttribute("followeeCount", followeeCount);
// 粉丝数量
long followerCount =
followService.findFollowerCount(ENTITY_TYPE_USER, userId);
model.addAttribute("followerCount", followerCount);
// 当前登录的用户是否已关注显示的该个人主页的用户
boolean hasFollowed = false;
if (hostHolder.getUser() != null) {
hasFollowed = followService.hasFollowed(
hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId);
}
model.addAttribute("hasFollowed", hasFollowed);
return "/site/profile";
}
4.5、关注列表、粉丝列表
FollowService
分页显示
// ==========================查询某用户userID关注的人============================
public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) { //实体类型entityType是 ENTITY_TYPE_USER
String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
//range范围查询默认是从小到大,reverseRange从大到小
Set<Integer> targetIds =
redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);//起始索引和截止索引
if (targetIds == null) {
return null;
}
List<Map<String, Object>> list = new ArrayList<>();
for (Integer targetId : targetIds) {
Map<String, Object> map = new HashMap<>();
User user = userService.findUserById(targetId);
map.put("user", user);
Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
map.put("followTime", new Date(score.longValue()));
list.add(map);//一个map封装一个被关注的user的信息
}
return list;
}
// ==========================查询某用户的粉丝===========================
public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);
if (targetIds == null) {
return null;
}
List<Map<String, Object>> list = new ArrayList<>();
for (Integer targetId : targetIds) {
Map<String, Object> map = new HashMap<>();
User user = userService.findUserById(targetId);
map.put("user", user);
Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
map.put("followTime", new Date(score.longValue()));
list.add(map);
}
return list;
}
FollowController
//=============查询某一用户关注的人,在路径中传入当前用户的userID============
@RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET)
public String getFollowees(@PathVariable("userId") int userId, Page page, Model model) {
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
model.addAttribute("user", user);
page.setLimit(5);
page.setPath("/followees/" + userId);
page.setRows((int) followService.findFolloweeCount(userId, ENTITY_TYPE_USER));
List<Map<String, Object>> userList = followService.findFollowees(userId, page.getOffset(), page.getLimit());//page.getOffset()当前页的起始行
if (userList != null) {
for (Map<String, Object> map : userList) {
User u = (User) map.get("user");
//判断当前用户是否关注了当前页面被关注的用户
map.put("hasFollowed", hasFollowed(u.getId()));
}
}
model.addAttribute("users", userList);
return "/site/followee";
}
//==========================查询某用户userId的粉丝===========================
@RequestMapping(path = "/followers/{userId}", method = RequestMethod.GET)
public String getFollowers(@PathVariable("userId") int userId, Page page, Model model) {
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("该用户不存在!");
}
model.addAttribute("user", user);
page.setLimit(5);
page.setPath("/followers/" + userId);
page.setRows((int) followService.findFollowerCount(ENTITY_TYPE_USER, userId));
List<Map<String, Object>> userList = followService.findFollowers(userId, page.getOffset(), page.getLimit());
if (userList != null) {
for (Map<String, Object> map : userList) {
User u = (User) map.get("user");
map.put("hasFollowed", hasFollowed(u.getId()));
}
}
model.addAttribute("users", userList);
return "/site/follower";
}
private boolean hasFollowed(int userId) {
if (hostHolder.getUser() == null) {//先判断是否登录
return false;
}
return followService.hasFollowed(
hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId);
}
profile.html
followee.html
4.6、优化登录模块
之前的验证码是存储在session里的,登录凭证存储在数据库中,每次请求都要被拦截然后再数据库中查询,用户的信息也是每次都从数据库中查询得到,效率低。
mysql中login_ticket表作废,user表仍然保存,只是同时缓存在redis中,过了有效时间就过期被清除。
(1)使用redis存储验证码
RedisKeyUtil
验证码应该和用户关联,但是一开始还没登录,因此不能传入userID,为了识别用户是谁,在客户端访问登录页面的时候,给用户发送一个随机生成的凭证owner,存在cookie中并设置过期时间
private static final String PREFIX_KAPTCHA = "kaptcha";//验证码
// 登录验证码
public static String getKaptchaKey(String owner) {
return PREFIX_KAPTCHA + SPLIT + owner;
}
LoginController
getKaptcha
方法之中,替换之前的 session.setAttribute("kaptcha", text)
,将验证码缓存入redis
@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
public void getKaptcha(HttpServletResponse response/*, HttpSession session*/) {
// 生成验证码
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
// 将验证码存入session
// session.setAttribute("kaptcha", text);
// 验证码的归属
String kaptchaOwner = CommunityUtil.generateUUID();
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60);//有效时间
cookie.setPath(contextPath);//整个项目的路径都有效
response.addCookie(cookie);
// 将验证码存入Redis
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);
// 将突图片输出给浏览器
response.setContentType("image/png");
try {
OutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
} catch (IOException e) {
logger.error("响应验证码失败:" + e.getMessage());
}
}
存入验证码至redis之后,在登录时需要使用,判断用户输入的验证码是否正确
login
方法需要使用kaptchaOwner
,从 请求参数的Cookie中取即可
@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) {
// 检查验证码
// String kaptcha = (String) session.getAttribute("kaptcha");
String kaptcha = null;
if (StringUtils.isNotBlank(kaptchaOwner)) {//验证码没有失效
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
kaptcha = (String) redisTemplate.opsForValue().get(redisKey);
}
if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
model.addAttribute("codeMsg", "验证码不正确!");
return "/site/login";
}
(2)使用redis存储登录凭证
RedisKeyUtil
private static final String PREFIX_TICKET = "ticket";
//登录的凭证
public static String getTicketKey(String ticket){
return PREFIX_TICKET + SPLIT + ticket;
}
UserService
login方法
public Map<String, Object> login(String username, String password, int expiredSeconds) {
Map<String, Object> map = new HashMap<>();
// 空值处理
if (StringUtils.isBlank(username)) {
map.put("usernameMsg", "账号不能为空!");
return map;
}
if (StringUtils.isBlank(password)) {
map.put("passwordMsg", "密码不能为空!");
return map;
}
// 验证账号
User user = userMapper.selectByName(username);
if (user == null) {
map.put("usernameMsg", "该账号不存在!");
return map;
}
// 验证状态
if (user.getStatus() == 0) {
map.put("usernameMsg", "该账号未激活!");
return map;
}
// 验证密码
password = CommunityUtil.md5(password + user.getSalt());
if (!user.getPassword().equals(password)) {
map.put("passwordMsg", "密码不正确!");
return map;
}
// 生成登录凭证
LoginTicket loginTicket = new LoginTicket();
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(0);
loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
// loginTicketMapper.insertLoginTicket(loginTicket);
//============================以下为修改==========================
String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
redisTemplate.opsForValue().set(redisKey, loginTicket);
map.put("ticket", loginTicket.getTicket());
return map;
}
logout方法:
失效用户登录凭证,不是真正地删除,只是修改状态即可。 先查询再修改状态然后再重新存入redis缓存中去。不删除的原因是将来可能需要查询用户的历史登录时间等。
public void logout(String ticket) {
// loginTicketMapper.updateStatus(ticket, 1);
String redisKey = RedisKeyUtil.getTicketKey(ticket);
LoginTicket loginTicket =
(LoginTicket)redisTemplate.opsForValue().get(redisKey);
loginTicket.setStatus(1);//1:失效状态
redisTemplate.opsForValue().set(redisKey, loginTicket);
}
findLoginTicket 查询凭证的方法:
public LoginTicket findLoginTicket(String ticket){
//return loginTicketMapper.selectByTicket(ticket);
String redisKey = RedisKeyUtil.getTicketKey(ticket);
return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
}
(3)使用Redis缓存用户信息
RedisKeyUtil
private static final String PREFIX_USER = "user";
//用户
public static String getUserKey(int userId){
return PREFIX_USER + SPLIT + userId;
}
UserService
额外添加几个方法
//1.优先从缓存中取值
private User getCatche(int userId){
String redisKey = RedisKeyUtil.getUserKey(userId);
return (User) redisTemplate.opsForValue().get(redisKey);
}
//2.取不到时初始化缓存数据
private User initCache(int userId){
//需要从mysql中查询数据
User user = userMapper.selectById(userId);
String redisKey = RedisKeyUtil.getUserKey(userId);
//注意:需要设置一个过期时间
redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
return user;
}
//3. 数据变更时清除缓存数据
private void clearCache(int userId){
String redisKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.delete(redisKey);
}
修改根据用户id获取User的方法 :
public User findUserById(int id){
//现在缓存中查找,如果缓存中没有值的话,则需要初始化缓存的值
User user = getCatche(id);
if(user == null){
user = initCache(id);
}
return user;
}
在有修改User对象的地方,要先清空缓存,然后再重新加入缓存(从数据库中读取)
activation方法:
public int activation(int userId, String code) {
User user = userMapper.selectById(userId);
if (user.getStatus() == 1) {
return ACTIVATION_REPEAT;
} else if (user.getActivationCode().equals(code)) {
userMapper.updateStatus(userId, 1);
clearCache(userId);
return ACTIVATION_SUCCESS;
} else {
return ACTIVATION_FAILURE;
}
}
updateHeader方法:
public int updateHeader(int userId, String headerUrl){
int rows = userMapper.updateHeader(userId, headerUrl);
clearCache(userId);
return rows;
}