目录:
1.实现概述
2.jedis使用
3.JedisAdapter-包装基本的jedis方法
4.RedisKeyUtil-生成redisKey
5.LikeService-业务实现
6.LikeController-前端交互
7.HomeController
1.实现概述
为什么要单独将点赞功能拿出来说,因为点赞是一个数据交换非常频繁的功能实现,如果使用mysql数据库来存储数据的话,数据库压力会很大,因此采用基于内存的redis数据库来实现。
redis的安装和简单使用请参照:https://www.cnblogs.com/fyboke/p/6264922.html
redis数据库是一种key-value型的数据库,它支持存储的value类型很多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
因此我们要实现点赞功能,只需用key来存储我们当前正在哪条news,用value存储一个点赞此news的用户的set即可。
2.jedis使用
jedis介绍:jedis是redis数据库的java语言的驱动jar包,用来操作redis数据库,其中封装了redis的基本操作。类似与mysql-jdbc连接桥。
需要在pom.xml文件中加入jedis的依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
3.jedisAdapter
此类是采用了@Service注释,用来包装基本的jedis操作。主要是sadd,srem和sisremember。
package com.nowcoder.util;
import com.alibaba.fastjson.JSON;
import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventType;
import com.nowcoder.controller.IndexController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ExceptionHandler;
import redis.clients.jedis.*;
import java.util.List;
/**
* Created by nowcoder on 2016/7/13.
*/
@Service
public class JedisAdapter implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(JedisAdapter.class);
private Jedis jedis = null;
private JedisPool pool = null;
@Override
public void afterPropertiesSet() throws Exception {
//在初始化之后初始化pool
//jedis = new Jedis("localhost");//直接使用jedis
pool = new JedisPool("localhost", 6379);//使用jedis池
}
private Jedis getJedis() {
//return jedis;
return pool.getResource();
}
public long sadd(String key, String value) {//将k-v存入redis的set中
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.sadd(key, value);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return 0;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public long srem(String key, String value) {//将k-v从redis的set中移除
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.srem(key, value);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return 0;
} finally {
if (jedis != null) {
jedis.close();//使用完池中的jedis,即使关闭
}
}
}
public boolean sismember(String key, String value) {//判断set中是否含有目标k-v
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.sismember(key, value);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return false;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public long scard(String key) {//返回set中有多少个元素
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.scard(key);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return 0;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
//**************************************************
public void setex(String key, String value) {
// 验证码, 防机器注册,记录上次注册时间,有效期3天
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.setex(key, 10, value);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public long lpush(String key, String value) {
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.lpush(key, value);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return 0;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public List<String> brpop(int timeout, String key) {
Jedis jedis = null;
try {
jedis = pool.getResource();
return jedis.brpop(timeout, key);
} catch (Exception e) {
logger.error("发生异常" + e.getMessage());
return null;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
public void setObject(String key, Object obj) {
set(key, JSON.toJSONString(obj));
}
public <T> T getObject(String key, Class<T> clazz) {
String value = get(key);
if (value != null) {
return JSON.parseObject(value, clazz);
}
return null;
}
}
4.RedisKeyUtil
前面提到过采用k-v来存储点赞情况,那么针对每一条新闻,我们必须有一个特定的key。此类用来生成此key。
package com.nowcoder.util;
/**
* Created by nowcoder on 2016/7/13.
*/
public class RedisKeyUtil {
private static String SPLIT = ":";
private static String BIZ_LIKE = "LIKE";
private static String BIZ_DISLIKE = "DISLIKE";
private static String BIZ_EVENT = "EVENT";
public static String getEventQueueKey() {
return BIZ_EVENT;
}
//虽然这里只是实现了对news的点赞功能,但是为了增强代码的可复用性,int entityId, int entityType可以表示某一个元素而不单是news
public static String getLikeKey(int entityId, int entityType) {
return BIZ_LIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId);
}
public static String getDisLikeKey(int entityId, int entityType) {
return BIZ_DISLIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId);
}
}
5.LikeService
此类通过调用jedisAdapter中的方法,来实现点赞、踩的功能实现。
package com.nowcoder.service;
import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by nowcoder on 2016/7/13.
*/
@Service
public class LikeService {
@Autowired
JedisAdapter jedisAdapter;
//判断某用户对某个元素的赞踩状态,喜欢返回1,不喜欢返回-1,否则返回0
public int getLikeStatus(int userId, int entityType, int entityId) {
String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
if(jedisAdapter.sismember(likeKey, String.valueOf(userId))) {
return 1;
}
String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
return jedisAdapter.sismember(disLikeKey, String.valueOf(userId)) ? -1 : 0;
}
//实现赞功能
public long like(int userId, int entityType, int entityId) {
// 在喜欢集合里增加
String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
jedisAdapter.sadd(likeKey, String.valueOf(userId));
// 从反对里删除
String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
jedisAdapter.srem(disLikeKey, String.valueOf(userId));
return jedisAdapter.scard(likeKey);
}
//实现踩功能
public long disLike(int userId, int entityType, int entityId) {
// 在反对集合里增加
String disLikeKey = RedisKeyUtil.getDisLikeKey(entityId, entityType);
jedisAdapter.sadd(disLikeKey, String.valueOf(userId));
// 从喜欢里删除
String likeKey = RedisKeyUtil.getLikeKey(entityId, entityType);
jedisAdapter.srem(likeKey, String.valueOf(userId));
return jedisAdapter.scard(likeKey);
}
}
6.LikeController
从前端获取newsId,从hostHolder中获取userId,完成前后端交互。
package com.nowcoder.controller;
import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventProducer;
import com.nowcoder.async.EventType;
import com.nowcoder.model.EntityType;
import com.nowcoder.model.HostHolder;
import com.nowcoder.model.News;
import com.nowcoder.service.LikeService;
import com.nowcoder.service.NewsService;
import com.nowcoder.util.ToutiaoUtil;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Created by nowcoder on 2016/7/13.
*/
@Controller
public class LikeController {
@Autowired
LikeService likeService;
@Autowired
HostHolder hostHolder;//以便获取登录用户
@Autowired
NewsService newsService;
@Autowired
EventProducer eventProducer;//异步
@RequestMapping(path = {"/like"}, method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String like(@Param("newId") int newsId) {
//调用喜欢功能
long likeCount = likeService.like(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);
// 更新mysql数据库的喜欢数
News news = newsService.getById(newsId);
newsService.updateLikeCount(newsId, (int) likeCount);
//异步队列,以后说
eventProducer.fireEvent(new EventModel(EventType.LIKE)
.setEntityOwnerId(news.getUserId())
.setActorId(hostHolder.getUser().getId()).setEntityId(newsId));
//通过json串,将赞数量返回前端
return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
}
@RequestMapping(path = {"/dislike"}, method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String dislike(@Param("newId") int newsId) {
long likeCount = likeService.disLike(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);
// 更新喜欢数
newsService.updateLikeCount(newsId, (int) likeCount);
return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
}
}
7.HomeController
因为首页,如果一个用户点赞,那么它对应显示的“赞”键的效果应该为“已按”的状态,所以homeController中还需获取当前登录的user,在从redis的set中读取是否含有该userId。
NewsController与此类似。