目录树
🌕Redis一站式高性能存储(点赞功能)
一、点赞
把数据存到redis里,而存到redis里就好像我们操作一个map一样,非常简单。因此,数据访问层就不单独写了,因为太简单了,直接写业务层。在业务过程中,我们要需要存数据,直接调用redisTemplate往里存就行了,取也是同样的。
然后,在项目redis存数据取数据的操作数据过程中,是以key为关键的,是面向key去编程的,所以为了能让key反复复用,最好给redis写一个工具,专门用来生成它的key,方便复用。
1.1 RedisKeyUtil
package com.yty.community.util;
/*生成redis的key的工具类,方便复用*/
public class RedisKeyUtil {
private static final String SPLIT = ":";
private static final String PREFIX_ENTITY_LIKE = "like:entity";
//1.生成某个实体的赞 这样一个key
//实体就要传入实体的两个关键信息Type和Id
//key:like:entity:entityType:entityId 对应的值--> set(userId)
public static String getEntityLikeKey(int entityType,int entityId){
return PREFIX_ENTITY_LIKE + SPLIT +entityType +SPLIT +entityId;
}
}
1.2 LikeService
@Service
public class LikeService {
//要把数据存到redis,所以注入redisTemplate
@Autowired
private RedisTemplate redisTemplate;
//一、点赞
public void like(int userId,int entityType,int entityId){
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); //获得(声明)key
//❤判断userId在不在里面(在就说明已经点赞了,不在,说明未点赞)
Boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
if (isMember){
redisTemplate.opsForSet().remove(entityLikeKey,userId);//如果在,点击的作用就是去取消喜欢,所以去除掉userId
}else {
redisTemplate.opsForSet().add(entityLikeKey,userId);//如果在,点击的作用就是去添加喜欢,所以去添加userId
}
}
//二、查询某实体点赞数量(也就是被点赞数)
public long findEntityLikeCount(int entityType,int entityId){
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); //获得(声明)key
return redisTemplate.opsForSet().size(entityLikeKey);
}
//三、查询某人对某实体的点赞状态(有没有点过赞)
public int findEntityLikeStatus(int userId,int entityType,int entityId){
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); //获得(声明)key
return redisTemplate.opsForSet().isMember(entityLikeKey,userId) ? 1:0;
//1 true点了赞 0 false没点赞
}
}
1.3 LikeController
@Controller
public class LikeController {
@Autowired
private LikeService likeService;
@Autowired
private HostHolder hostHolder;
@RequestMapping(path ="/like",method = RequestMethod.POST)
@ResponseBody
public String like(int entityType,int entityId){
//获取当前用户 _ 不用判断登录状态,因为有拦截器,添加一下即可。后期security也可以的。
User user = hostHolder.getUsers();
//点赞
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);
}
}
1.3.1 HomeController
这里加逻辑是为了让 首页每个帖子的赞的数量能实时显示,所以加一个逻辑上去
1.3.2 DiscussPostController
同样的,帖子详情页也要进行帖子点赞量的实时显示。所以也要添加逻辑
对帖子、评论、评论回复都要添加对应逻辑
1.4 页面测试
这里两个赞是我用了两个账号进行点赞的。
换第三个账号截图的。
然后我再给它点个赞,退出账号游客观看点赞数
成功!
1.5 Redis测试结果
二、我收到的赞
2.1 RedisKeyUtil
/*生成redis的key的工具类,方便复用*/
public class RedisKeyUtil {
...
private static final String PREFIX_USER_LIKE = "like:user";
...
//2.某一个用户收到的赞
//key:like:user:userId 对应的值--> int
public static String getUserLikeKey(int userId){
return PREFIX_USER_LIKE + SPLIT + userId;
}
}
2.2 LikeService
对“点赞”方法改一下,加一个维度计入那个数量,也就是一个业务当中会连续执行两次更新操作,所以整个业务要保证事务性。
那Redis怎么保证事务呢?编程式事务
解决。所以将点赞方法内的代码进行重构
//一、点赞
public void like(int userId,int entityType,int entityId,int entityUserId){
// String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); //获得(声明)key
// //❤判断userId在不在里面(在就说明已经点赞了,不在,说明未点赞)
// Boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
// if (isMember){
// redisTemplate.opsForSet().remove(entityLikeKey,userId);//如果在,点击的作用就是去取消喜欢,所以去除掉userId
// }else {
// redisTemplate.opsForSet().add(entityLikeKey,userId);//如果在,点击的作用就是去添加喜欢,所以去添加userId
// }
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); //获得(声明)key
String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId);//获得(声明)key,entityUserId是被赞人的id,userId是点赞人的id
boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId); //查看当前用户有没有对实体点过赞
//这里遵循了查询不放在事务中,要么在开始,要么在结束
operations.multi();//开启事务
if (isMember){//如果在,点击的作用就是去取消
operations.opsForSet().remove(entityLikeKey,userId);
operations.opsForValue().decrement(userLikeKey); //点赞量-1
}else {//如果不在,点击的作用就是去添加
operations.opsForSet().add(entityLikeKey,userId);
operations.opsForValue().increment(userLikeKey); //点赞量+1
}
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();//如果count为空就返回0,否则就返回count的整数值
}
2.3 LikeController
2.4 UserController
//个人主页(不止查看当前用户的主页,更是任意人的主页,所以这里传过来用户id,就知道去谁的主页)
@RequestMapping(path = "/profile/{userId}",method = RequestMethod.GET)
public String getProfilePage(@PathVariable("userId")int userId,Model model){
User user = userService.findByUserId(userId);
if (user == null){
throw new RuntimeException("该用户不存在!");
}
//用户
model.addAttribute("user",user);
//查点赞数量
int likeCount = likeService.findUserLikeCount(userId);
model.addAttribute("likeCount",likeCount);
return "/site/profile";
}
2.5 页面修改
profile.html
2.6 测试
这是因为重构了方法,所以之前的数据用不了了。我这里刷新一下,重新测试。
一看首页:
点赞数确实没了,然后点赞帖子,退出账号换一个账号查看:
数量为1,成功!