0.前言
本文基于Springboot利用Redis實(shí)現(xiàn)點(diǎn)贊功能的緩存和定時(shí)持久化接口。
用戶對(duì)瀏覽內(nèi)容進(jìn)行【點(diǎn)贊/取贊】,并發(fā)送【點(diǎn)贊/取贊】請(qǐng)求到后端,這些信息先存入Redis中緩存,再每隔兩小時(shí)將Redis中的內(nèi)容直接寫入數(shù)據(jù)庫持久化存儲(chǔ)。
1.項(xiàng)目目錄結(jié)構(gòu)
2.Redis緩存點(diǎn)贊消息
1.設(shè)計(jì)思路
用戶點(diǎn)贊一條數(shù)據(jù),設(shè)置狀態(tài)為0,并且更新被點(diǎn)贊內(nèi)容的likeCount+1
用戶取消點(diǎn)贊一條數(shù)據(jù),設(shè)置狀態(tài)為1,并且更新被點(diǎn)贊內(nèi)容的likeCount+0
1.1 Redis鍵值對(duì)設(shè)計(jì)
選用Hash(散列)存儲(chǔ)
點(diǎn)贊信息
- key: (String) 瀏覽信息id和點(diǎn)贊用戶id拼接而成, 分隔符為::
- value: (HashMap) 存儲(chǔ)點(diǎn)贊狀態(tài)(0: 點(diǎn)贊 1:取消點(diǎn)贊)和更新時(shí)間的時(shí)間戳
-
key: "瀏覽信息id::點(diǎn)贊用戶id" value:
{status: Integer, updateTime: long}
點(diǎn)贊數(shù)量
- key: (String) 瀏覽信息id
- value: (Integer) 點(diǎn)贊數(shù)量
1.2 點(diǎn)贊
- 用戶點(diǎn)贊信息,發(fā)送點(diǎn)贊請(qǐng)求到后端
-
后端判斷該點(diǎn)贊信息在Redis中的狀態(tài)
- Redis不進(jìn)行存儲(chǔ),并提醒前端重復(fù)存儲(chǔ)。
- 更新/新增點(diǎn)贊信息
- 更新/新增點(diǎn)贊量
- 【不存在(沒有對(duì)應(yīng)key) 】|| 【取消點(diǎn)贊(即取出的status為1)】
- 【點(diǎn)贊(即取出的status為0,此時(shí)相當(dāng)于重復(fù)點(diǎn)贊行為)】
- 一次點(diǎn)贊請(qǐng)求完畢
1.3 取消點(diǎn)贊
- 用戶取消點(diǎn)贊信息,發(fā)送取消點(diǎn)贊請(qǐng)求到后端
-
后端判斷該點(diǎn)贊信息在Redis中的狀態(tài)
- 更新/新增點(diǎn)贊信息
- 更新/新增點(diǎn)贊量
- Redis不進(jìn)行存儲(chǔ),并提醒前端重復(fù)存儲(chǔ)。
- 更新/新增點(diǎn)贊信息
- 增加0條內(nèi)容點(diǎn)贊量
- 【不存在(沒有對(duì)應(yīng)key) 】
- 【取消點(diǎn)贊(即取出的status為1,此時(shí)相當(dāng)于重復(fù)取消點(diǎn)贊行為)】
- 【點(diǎn)贊(即取出的status為0)】
- 一次取消點(diǎn)贊請(qǐng)求完畢
2.核心代碼實(shí)現(xiàn)
2.1 Redis封裝
具體實(shí)現(xiàn)參考該博客,不在贅述。
- https://www.cnblogs.com/caizhaokai/p/11037610.html
2.2 工具類
1.時(shí)間戳轉(zhuǎn)化為LocalDateTime
importjava.time.Instant;
importjava.time.LocalDateTime;
importjava.time.ZoneId;
/**
*工具類:將時(shí)間戳轉(zhuǎn)化為LocalDateTime
*主要是因?yàn)閞edis不好存儲(chǔ)LocalDateTime,存儲(chǔ)timestamp方便一點(diǎn),而且格式可以隨意改變
*/
publicclassLocalDateTimeConvertUtil{
publicstaticLocalDateTimegetDateTimeOfTimestamp(longtimestamp){
Instantinstant=Instant.ofEpochMilli(timestamp);
ZoneIdzone=ZoneId.systemDefault();
returnLocalDateTime.ofInstant(instant,zone);
}
}
2.RedisKey處理類
publicclassRedisKeyUtils{
/**
*
保存用戶點(diǎn)贊內(nèi)容數(shù)據(jù)的key
*@date2021/9/2614:44
*/
publicstaticfinalStringMAP_KEY_USER_LIKED="MAP_USER_LIKED";
/**
*
保存內(nèi)容被點(diǎn)贊數(shù)量的key
*@date2021/9/2614:44
*/
publicstaticfinalStringMAP_KEY_USER_LIKED_COUNT="MAP_USER_LIKED_COUNT";
/**
*拼接被點(diǎn)贊的內(nèi)容id和點(diǎn)贊的人的id作為key。格式222222::333333
*@paraminfoId被點(diǎn)贊的內(nèi)容id
*@paramlikeUserId點(diǎn)贊的人的id
*@return
*/
publicstaticStringgetLikedKey(StringinfoId,StringlikeUserId){
returninfoId+
"::"+
likeUserId;
}
}
2.3 DTO
//UserLikesDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
publicclassUserLikesDTO{
privateStringinfoId;
privateStringlikeUserId;
privateIntegerstatus;
privateLocalDateTimeupdateTime;
}
//UserLikeCountDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
publicclassUserLikeCountDTO{
privateStringinfoId;
privateIntegerlikeCount;
}
2.4 Service
- interface
//RedisService.java
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikeCountDTO;
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikesDTO;
importjava.util.List;
/**
*負(fù)責(zé)將數(shù)據(jù)寫入Redis緩存
*/
publicinterfaceRedisService{
/**
*獲取點(diǎn)贊狀態(tài)
*@paraminfoId
*@paramlikeUserId
*/
IntegergetLikeStatus(StringinfoId,StringlikeUserId);
/**
*點(diǎn)贊。狀態(tài)為1
*@paraminfoId
*@paramlikeUserId
*/
voidsaveLiked2Redis(StringinfoId,StringlikeUserId);
/**
*取消點(diǎn)贊。將狀態(tài)改變?yōu)?
*@paraminfoId
*@paramlikeUserId
*/
voidunlikeFromRedis(StringinfoId,StringlikeUserId);
/**
*從Redis中刪除一條點(diǎn)贊數(shù)據(jù)
*@paraminfoId
*@paramlikeUserId
*/
voiddeleteLikedFromRedis(StringinfoId,StringlikeUserId);
/**
*該內(nèi)容的點(diǎn)贊數(shù)變化Δdelta
*@paraminfoId
*/
voidin_decrementLikedCount(StringinfoId,Integerdelta);
/**
*獲取Redis中存儲(chǔ)的所有點(diǎn)贊數(shù)據(jù)
*@return
*/
ListgetLikedDataFromRedis() ;
/**
*獲取Redis中存儲(chǔ)的所有點(diǎn)贊數(shù)量
*@return
*/
ListgetLikedCountFromRedis() ;
}
- implement
importcom.csu.edu.redisLikeDemo.common.CONSTANT;
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikeCountDTO;
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikesDTO;
importcom.csu.edu.redisLikeDemo.service.RedisService;
importcom.csu.edu.redisLikeDemo.util.LocalDateTimeConvertUtil;
importcom.csu.edu.redisLikeDemo.util.RedisKeyUtils;
importlombok.extern.slf4j.Slf4j;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.data.redis.core.Cursor;
importorg.springframework.data.redis.core.HashOperations;
importorg.springframework.data.redis.core.ScanOptions;
importorg.springframework.stereotype.Service;
importjava.time.LocalDateTime;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
@Service("redisService")
@Slf4j
publicclassRedisServiceImplimplementsRedisService{
@Autowired
privateHashOperationsredisHash;//RedisHash
@Override
publicIntegergetLikeStatus(StringinfoId,StringlikeUserId){
if(redisHash.hasKey(RedisKeyUtils.MAP_KEY_USER_LIKED,RedisKeyUtils.getLikedKey(infoId,likeUserId))){
HashMapmap=(HashMap)redisHash.get(RedisKeyUtils.MAP_KEY_USER_LIKED,RedisKeyUtils.getLikedKey(infoId,likeUserId));
return(Integer)map.get("status");
}
returnCONSTANT.LikedStatusEum.NOT_EXIST.getCode();
}
@Override
publicvoidsaveLiked2Redis(StringinfoId,StringlikeUserId){
//生成key
Stringkey=RedisKeyUtils.getLikedKey(infoId,likeUserId);
//封裝value喜歡狀態(tài)更新時(shí)間
HashMapmap=newHashMap<>();
map.put("status",CONSTANT.LikedStatusEum.LIKE.getCode());
map.put("updateTime",System.currentTimeMillis());
redisHash.put(RedisKeyUtils.MAP_KEY_USER_LIKED,key,map);
}
@Override
publicvoidunlikeFromRedis(StringinfoId,StringlikeUserId){
//生成key
Stringkey=RedisKeyUtils.getLikedKey(infoId,likeUserId);
//封裝value喜歡狀態(tài)更新時(shí)間
HashMapmap=newHashMap<>();
map.put("status",CONSTANT.LikedStatusEum.UNLIKE.getCode());
map.put("updateTime",System.currentTimeMillis());//存入當(dāng)前時(shí)間戳
redisHash.put(RedisKeyUtils.MAP_KEY_USER_LIKED,key,map);
}
@Override
publicvoiddeleteLikedFromRedis(StringinfoId,StringlikeUserId){
Stringkey=RedisKeyUtils.getLikedKey(infoId,likeUserId);
redisHash.delete(RedisKeyUtils.MAP_KEY_USER_LIKED,key);
}
@Override
publicvoidin_decrementLikedCount(StringinfoId,Integerdelta){
redisHash.increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,infoId,delta);
}
@Override
publicListgetLikedDataFromRedis() {
//scan讀取數(shù)據(jù),比key匹配優(yōu)雅
Cursor>cursor=redisHash.scan(RedisKeyUtils.MAP_KEY_USER_LIKED,ScanOptions.NONE);
Listlist=newArrayList<>();
while(cursor.hasNext()){
Map.Entryentry=cursor.next();
Stringkey=(String)entry.getKey();
//分離出infoId,likedPostId,解析value
String[]split=key.split("::");
StringinfoId=split[0];
StringlikeUserId=split[1];
HashMapmap=(HashMap)entry.getValue();
Integerstatus=(Integer)map.get("status");
longupdateTimeStamp=(long)map.get("updateTime");
LocalDateTimeupdateTime=LocalDateTimeConvertUtil.getDateTimeOfTimestamp(updateTimeStamp);//時(shí)間戳轉(zhuǎn)為LocalDateTime
//組裝成UserLike對(duì)象
UserLikesDTOuserLikesDTO=newUserLikesDTO(infoId,likeUserId,status,updateTime);
list.add(userLikesDTO);
//存到list后從Redis中清理緩存
redisHash.delete(RedisKeyUtils.MAP_KEY_USER_LIKED,key);
}
returnlist;
}
@Override
publicListgetLikedCountFromRedis() {
//scan讀取數(shù)據(jù),比key匹配優(yōu)雅
Cursor>cursor=redisHash.scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,ScanOptions.NONE);
Listlist=newArrayList<>();
while(cursor.hasNext()){
Map.Entrymap=cursor.next();
//將點(diǎn)贊數(shù)量存儲(chǔ)在LikedCountDT
Stringkey=(String)map.getKey();
UserLikeCountDTOdto=newUserLikeCountDTO(key,(Integer)map.getValue());
list.add(dto);
//從Redis中刪除這條記錄
redisHash.delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,key);
}
returnlist;
}
}
2.5 controller & API
- controller
importcom.csu.edu.redisLikeDemo.common.CommonResponse;
importcom.csu.edu.redisLikeDemo.service.LikeService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test/")
publicclassLikeController{
@Autowired
privateLikeServicelikeService;
@PostMapping("like")
publicCommonResponse{
returnlikeService.likeInfo(infoId,userId);
}
@PostMapping("dislike")
publicCommonResponse{
returnlikeService.dislikeInfo(infoId,userId);
}
}
- 接口測(cè)試
3.Redis定時(shí)持久化
1.設(shè)計(jì)思路
1.1 數(shù)據(jù)庫設(shè)計(jì)
#瀏覽內(nèi)容表
DROPTABLEIFEXISTS`view_item`;
CREATETABLE`view_item`(
`id`varchar(32)CHARACTERSETutf8COLLATEutf8_general_ciNOTNULLCOMMENT'內(nèi)容id(如文章、短視頻等等)',
`create_user`varchar(50)CHARACTERSETutf8COLLATEutf8_general_ciNULLDEFAULTNULLCOMMENT'創(chuàng)建者',
`like_count`int(11)NULLDEFAULTNULLCOMMENT'點(diǎn)贊數(shù)',
`cmt_count`int(11)NULLDEFAULTNULLCOMMENT'評(píng)論數(shù)',
`share_count`int(11)NULLDEFAULTNULLCOMMENT'分享數(shù)',
`create_time`datetime(0)NULLDEFAULTNULLCOMMENT'創(chuàng)建時(shí)間',
`update_time`datetime(0)NULLDEFAULTNULLCOMMENT'更新時(shí)間',
PRIMARYKEY(`id`)USINGBTREE
)ENGINE=InnoDBCHARACTERSET=utf8COLLATE=utf8_general_ciROW_FORMAT=Dynamic;
SETFOREIGN_KEY_CHECKS=1;
#點(diǎn)贊-用戶表
DROPTABLEIFEXISTS`user_likes`;
CREATETABLE`user_likes`(
`id`varchar(32)CHARACTERSETutf8mb4COLLATEutf8mb4_general_ciNOTNULLCOMMENT'點(diǎn)贊信息ID',
`info_id`varchar(32)CHARACTERSETutf8mb4COLLATEutf8mb4_general_ciNULLDEFAULTNULLCOMMENT'點(diǎn)贊對(duì)象id',
`like_user_id`varchar(32)CHARACTERSETutf8mb4COLLATEutf8mb4_general_ciNULLDEFAULTNULLCOMMENT'點(diǎn)贊人ID',
`status`tinyint(4)NULLDEFAULT0COMMENT'0點(diǎn)贊1取消',
`create_time`datetime(0)NULLDEFAULTNULLCOMMENT'創(chuàng)建時(shí)間',
`update_time`datetime(0)NULLDEFAULTNULLCOMMENT'更新時(shí)間',
PRIMARYKEY(`id`)USINGBTREE,
UNIQUEINDEX`agdkey`(`like_user_id`,`info_id`)USINGBTREE
)ENGINE=InnoDBCHARACTERSET=utf8mb4COLLATE=utf8mb4_general_ciCOMMENT='點(diǎn)贊記錄表'ROW_FORMAT=Dynamic;
1.2 流程
- 遍歷Redis的【點(diǎn)贊信息】,僅改變數(shù)據(jù)庫中點(diǎn)贊信息的狀態(tài)
- 判斷當(dāng)前點(diǎn)贊信息是否在數(shù)據(jù)庫中
-
否,則更新數(shù)據(jù)
- 數(shù)據(jù)庫中新增點(diǎn)贊-用戶記錄
- 更新內(nèi)容的點(diǎn)贊量
- 轉(zhuǎn)到6
-
是
- 轉(zhuǎn)到第3步
- 判斷數(shù)據(jù)庫中的點(diǎn)贊狀態(tài)與緩存中的點(diǎn)贊狀態(tài)(status)
-
一致
- 狀態(tài)不改變
- 點(diǎn)贊數(shù)量-1(兩種情況邏輯分析有差異,但是最終結(jié)果均為-1)
- 結(jié)束
-
不一致,則需要針對(duì)具體情況改變
- 轉(zhuǎn)到步驟4
- 判斷數(shù)據(jù)庫點(diǎn)贊狀態(tài)
-
已經(jīng)點(diǎn)贊,需要更改為取消點(diǎn)贊
- 數(shù)據(jù)庫中修改為取消點(diǎn)贊狀態(tài)
- 更新緩存中的點(diǎn)贊數(shù)量-1(減去數(shù)據(jù)庫中持久化的一個(gè)點(diǎn)贊量,一會(huì)兒緩存會(huì)和數(shù)據(jù)庫點(diǎn)贊總量加和)
-
取消點(diǎn)贊,需要更改
- 數(shù)據(jù)庫中修改為點(diǎn)贊狀態(tài)
- 無需更新緩存中的點(diǎn)贊數(shù)量,因?yàn)榫彺嬷幸呀?jīng)+1(即該點(diǎn)贊數(shù)據(jù)的點(diǎn)贊量)
- 將緩存【點(diǎn)贊數(shù)量】持久化并清理緩存 此處修改數(shù)據(jù)庫中的點(diǎn)贊數(shù)量
- 完成緩存持久化
1.3定時(shí)寫入
使用 Quartz redis
定時(shí)任務(wù)持久化存儲(chǔ)到數(shù)據(jù)庫
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
2.核心代碼實(shí)現(xiàn)
2.1Bean
importcom.baomidou.mybatisplus.annotation.TableField;
importcom.baomidou.mybatisplus.annotation.TableId;
importcom.baomidou.mybatisplus.annotation.TableName;
importlombok.Data;
importjava.time.LocalDateTime;
@Data
@TableName("user_likes")
publicclassUserLikes{
@TableId
privateStringid;
@TableField("info_id")
privateStringinfoId;
@TableField("like_user_id")
privateStringlikeUserId;
privateIntegerstatus;
@TableField("create_time")
privateLocalDateTimecreateTime;
@TableField("update_time")
privateLocalDateTimeupdateTime;
}
importcom.baomidou.mybatisplus.annotation.TableField;
importcom.baomidou.mybatisplus.annotation.TableId;
importcom.baomidou.mybatisplus.annotation.TableName;
importlombok.Data;
importjava.time.LocalDateTime;
@Data
@TableName("view_item")
publicclassViewItem{
@TableId
privateStringid;
@TableField("create_user")
privateStringcreateUser;
@TableField("like_count")
privateIntegerlikeCount;
@TableField("cmt_count")
privateIntegercmtCount;
@TableField("share_count")
privateIntegershareCount;
@TableField("create_time")
privateLocalDateTimecreateTime;
@TableField("update_time")
privateLocalDateTimeupdateTime;
}
2.2 Service
- interface
importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;
importcom.csu.edu.redisLikeDemo.domain.UserLikes;
/**
*負(fù)責(zé)將Redis緩存中的數(shù)據(jù)持久化到數(shù)據(jù)庫中
*/
publicinterfaceDBService{
/**
*保存點(diǎn)贊記錄
*@paramuserLike
*@return
*/
Booleansave(UserLikesuserLike);
/**
*更新點(diǎn)贊記錄
*@paramuserLike
*@return
*/
Booleanupdate(UserLikesuserLike);
/**
*根據(jù)內(nèi)容的id查詢點(diǎn)贊列表(即查詢都誰給這個(gè)內(nèi)容點(diǎn)贊過)
*@paraminfoId內(nèi)容的id
*@return
*/
PagegetLikedListByInfoId(StringinfoId,intpageNum,intpageSize) ;
/**
*根據(jù)點(diǎn)贊人的id查詢點(diǎn)贊列表(即查詢這個(gè)人都給哪些內(nèi)容點(diǎn)贊過)
*@paramlikeUserId
*@return
*/
PagegetLikedListByLikeUserId(StringlikeUserId,intpageNum,intpageSize) ;
/**
*通過被點(diǎn)贊內(nèi)容和點(diǎn)贊人id查詢是否存在點(diǎn)贊記錄
*@paraminfoId
*@paramlikeUserId
*@return
*/
UserLikesgetByInfoIdAndLikeUserId(StringinfoId,StringlikeUserId);
/**
*將Redis里的點(diǎn)贊數(shù)據(jù)存入數(shù)據(jù)庫中,True表示還需要進(jìn)一步持久化,F(xiàn)alse表示數(shù)據(jù)庫中已存在該數(shù)據(jù),無需進(jìn)一步持久化
*/
voidtransLikedFromRedis2DB();
/**
*將Redis中的點(diǎn)贊數(shù)量數(shù)據(jù)存入數(shù)據(jù)庫
*/
voidtransLikedCountFromRedis2DB();
}
- implement
importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
importcom.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;
importcom.csu.edu.redisLikeDemo.common.CONSTANT;
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikeCountDTO;
importcom.csu.edu.redisLikeDemo.domain.DTO.UserLikesDTO;
importcom.csu.edu.redisLikeDemo.domain.UserLikes;
importcom.csu.edu.redisLikeDemo.domain.ViewItem;
importcom.csu.edu.redisLikeDemo.mapper.UserLikesMapper;
importcom.csu.edu.redisLikeDemo.mapper.ViewItemMapper;
importcom.csu.edu.redisLikeDemo.service.DBService;
importcom.csu.edu.redisLikeDemo.service.RedisService;
importlombok.extern.slf4j.Slf4j;
importorg.apache.commons.collections4.CollectionUtils;
importorg.n3r.idworker.Sid;
importorg.springframework.beans.BeanUtils;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Service;
importjava.time.LocalDateTime;
importjava.util.HashMap;
importjava.util.List;
@Service("dbService")
@Slf4j
publicclassDBServiceImplimplementsDBService{
@Autowired
privateRedisServiceredisService;
@Autowired
privateUserLikesMapperuserLikesMapper;
@Autowired
privateViewItemMapperviewItemMapper;
@Autowired
privateSidsid;//Id生成器,利用idWorker產(chǎn)生唯一(不重復(fù))自增式的id,可以根據(jù)需求選用其他方式,比如MyBatisPlus的自增
@Override
publicBooleansave(UserLikesuserLike){
introws=userLikesMapper.insert(userLike);
returnrows>0;
}
@Override
publicBooleanupdate(UserLikesuserLike){
UpdateWrapperupdateWrapper=newUpdateWrapper<>();
updateWrapper.set("status",userLike.getStatus());
updateWrapper.set("update_time",userLike.getUpdateTime());
updateWrapper.eq("id",userLike.getId());
introws=userLikesMapper.update(userLike,updateWrapper);
returnrows>0;
}
@Override
publicPagegetLikedListByInfoId(StringinfoId,intpageNum,intpageSize) {
//分頁獲取喜歡列表信息
Pageresult=newPage<>();
result.setCurrent(pageNum);
result.setSize(pageSize);
//獲取內(nèi)容的id查詢點(diǎn)贊列表
QueryWrapperqueryWrapper=newQueryWrapper();
queryWrapper.eq("info_id",infoId);
result=userLikesMapper.selectPage(result,queryWrapper);
log.info("獲得內(nèi)容的id查詢點(diǎn)贊列表(即查詢都有誰給這個(gè)內(nèi)容點(diǎn)贊過)");
returnresult;
}
@Override
publicPagegetLikedListByLikeUserId(StringlikeUserId,intpageNum,intpageSize) {
//分頁獲取喜歡列表信息
Pageresult=newPage<>();
result.setCurrent(pageNum);
result.setSize(pageSize);
//獲取用戶的id查詢點(diǎn)贊列表
QueryWrapperqueryWrapper=newQueryWrapper();
queryWrapper.eq("like_user_id",likeUserId);
result=userLikesMapper.selectPage(result,queryWrapper);
log.info("獲取點(diǎn)贊人的id查詢點(diǎn)贊列表(即查詢這個(gè)人都給哪些內(nèi)容點(diǎn)贊過)");
returnresult;
}
@Override
publicUserLikesgetByInfoIdAndLikeUserId(StringinfoId,StringlikeUserId){
HashMapmap=newHashMap<>();
map.put("info_id",infoId);
map.put("like_user_id",likeUserId);
try{
UserLikesuserLikes=userLikesMapper.selectByMap(map).get(0);
log.info("通過被點(diǎn)贊人和點(diǎn)贊人id查詢是否存在點(diǎn)贊記錄");
returnuserLikes;
}catch(Exceptione){
log.info("當(dāng)前查詢的被點(diǎn)贊人和點(diǎn)贊人id不存在點(diǎn)贊記錄");
returnnull;
}
}
@Override
publicvoidtransLikedFromRedis2DB(){
//批量獲取緩存中的點(diǎn)贊數(shù)據(jù)
Listlist=redisService.getLikedDataFromRedis();
if(CollectionUtils.isEmpty(list))//為空,不寫入
return;
for(UserLikesDTOitem:list){
UserLikesuserLikes=getByInfoIdAndLikeUserId(item.getInfoId(),item.getLikeUserId());//在數(shù)據(jù)庫中查詢
if(userLikes==null){//無記錄,新增
if(!save(userLikesDTOtoUserLikes(item))){
log.info("新增點(diǎn)贊數(shù)據(jù)失??!");
return;
//System.out.println("緩存記錄寫入數(shù)據(jù)庫失?。≌?qǐng)重試");
}
}else{//有記錄,更新
//判斷數(shù)據(jù)庫中點(diǎn)贊狀態(tài)與緩存中點(diǎn)贊狀態(tài)一致性
if(userLikes.getStatus()==item.getStatus()){//一致,無需持久化,點(diǎn)贊數(shù)量-1
redisService.in_decrementLikedCount(item.getInfoId(),-1);
}else{//不一致
if(userLikes.getStatus()==CONSTANT.LikedStatusEum.LIKE.getCode()){//在數(shù)據(jù)庫中已經(jīng)是點(diǎn)贊,則取消點(diǎn)贊,同時(shí)記得redis中的count-1
//之前是點(diǎn)贊,現(xiàn)在改為取消點(diǎn)贊1.設(shè)置更改status2.redis中的count要-1(消除在數(shù)據(jù)庫中自己的記錄)
userLikes.setStatus(CONSTANT.LikedStatusEum.UNLIKE.getCode());
redisService.in_decrementLikedCount(item.getInfoId(),-1);
}elseif(userLikes.getStatus()==CONSTANT.LikedStatusEum.UNLIKE.getCode()){//未點(diǎn)贊,則點(diǎn)贊,修改點(diǎn)贊狀態(tài)和點(diǎn)贊數(shù)據(jù)+1
userLikes.setStatus(CONSTANT.LikedStatusEum.LIKE.getCode());
redisService.in_decrementLikedCount(item.getInfoId(),1);
}
userLikes.setUpdateTime(LocalDateTime.now());
if(!update(userLikes)){//更新點(diǎn)贊數(shù)據(jù)
log.info("更新點(diǎn)贊數(shù)據(jù)失??!");
return;
//System.out.println("緩存記錄更新數(shù)據(jù)庫失敗!請(qǐng)重試");
}
}
}
}
}
@Override
publicvoidtransLikedCountFromRedis2DB(){
//獲取緩存中內(nèi)容的點(diǎn)贊數(shù)列表
Listlist=redisService.getLikedCountFromRedis();
if(CollectionUtils.isEmpty(list))//為空,不寫入
return;
for(UserLikeCountDTOitem:list){
ViewItemviewItem=viewItemMapper.selectById(item.getInfoId());
if(viewItem!=null){//新增點(diǎn)贊數(shù)
IntegerlikeCount=viewItem.getLikeCount()+item.getLikeCount();
System.out.println("內(nèi)容id不為空,更新內(nèi)容點(diǎn)贊數(shù)量");
viewItem.setLikeCount(likeCount);
UpdateWrapperupdateWrapper=newUpdateWrapper<>();
updateWrapper.set("like_count",viewItem.getLikeCount());
updateWrapper.eq("id",viewItem.getId());
introws=viewItemMapper.update(viewItem,updateWrapper);
if(rows>0){
System.out.println("成功更新內(nèi)容點(diǎn)贊數(shù)!");
return;
}
}
System.out.println("內(nèi)容id不存在,無法將緩存數(shù)據(jù)持久化!");
}
}
privateUserLikesuserLikesDTOtoUserLikes(UserLikesDTOuserLikesDTO){
UserLikesuserLikes=newUserLikes();
userLikes.setId(sid.nextShort());
BeanUtils.copyProperties(userLikesDTO,userLikes);
userLikes.setCreateTime(LocalDateTime.now());
userLikes.setUpdateTime(LocalDateTime.now());
returnuserLikes;
}
}
3. 定時(shí)更新數(shù)據(jù)庫
3.1 定時(shí)任務(wù)
importcom.csu.edu.redisLikeDemo.service.DBService;
importlombok.extern.slf4j.Slf4j;
importorg.quartz.JobExecutionContext;
importorg.quartz.JobExecutionException;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.scheduling.quartz.QuartzJobBean;
importjava.text.SimpleDateFormat;
importjava.util.Date;
/**
*定時(shí)任務(wù)
*/
@Slf4j
publicclassCronUtilextendsQuartzJobBean{
@Autowired
privateDBServicedbService;
privateSimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHHss");
/**
*執(zhí)行的定時(shí)任務(wù)
*/
@Override
protectedvoidexecuteInternal(JobExecutionContextcontext)throwsJobExecutionException{
log.info("LikeTask--------{}",sdf.format(newDate()));
//將Redis里的點(diǎn)贊信息同步到數(shù)據(jù)庫里
dbService.transLikedFromRedis2DB();
dbService.transLikedCountFromRedis2DB();
}
}
3.2 定時(shí)任務(wù)配置
設(shè)置每?jī)蓚€(gè)小時(shí)更新一次數(shù)據(jù)庫
importcom.csu.edu.redisLikeDemo.util.CronUtil;
importorg.quartz.*;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
/**
*開啟定時(shí)任務(wù)持久化存儲(chǔ)到數(shù)據(jù)庫
*/
@Configuration
publicclassQuartzConfig{
privatestaticfinalStringLIKE_TASK_IDENTITY="LikeTaskQuartz";
@Bean
publicJobDetailquartzDetail(){
returnJobBuilder.newJob(CronUtil.class).withIdentity(LIKE_TASK_IDENTITY).storeDurably().build();
}
@Bean
publicTriggerquartzTrigger(){
SimpleScheduleBuilderscheduleBuilder=SimpleScheduleBuilder.simpleSchedule()
//.withIntervalInSeconds(20)//設(shè)置時(shí)間周期單位秒
.withIntervalInHours(2)//兩個(gè)小時(shí)執(zhí)行一次
.repeatForever();
returnTriggerBuilder.newTrigger().forJob(quartzDetail())
.withIdentity(LIKE_TASK_IDENTITY)
.withSchedule(scheduleBuilder)
.build();
}
}
4.項(xiàng)目源碼地址 & 參考
項(xiàng)目源碼:
- https://github.com/WuYiheng-Og/redislike
參考:
https://cloud.tencent.com/developer/article/1445905
https://www.cnblogs.com/caizhaokai/p/11037610.html
-
緩存
+關(guān)注
關(guān)注
1文章
241瀏覽量
26758 -
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3848瀏覽量
64688 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14391 -
Redis
+關(guān)注
關(guān)注
0文章
378瀏覽量
10945 -
SpringBoot
+關(guān)注
關(guān)注
0文章
174瀏覽量
201
原文標(biāo)題:SpringBoot + Redis 實(shí)現(xiàn)點(diǎn)贊功能的緩存和定時(shí)持久化(附源碼)
文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論