社区开发4

Redis

NoSQL数据库:除了关系型数据库以外的数据库
Redis:基于键值对的NoSQL型数据库,key只是String,value支持多种数据结构:字符串(String)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等
Redis将所有的数据存到内存中,读写性能很好,同时Redis可以以快照或日志的形式把内存中的数据保存到硬盘上,以保证数据的安全
Redis的典型的应用场景:缓存、排行榜、计数器、社交网络、消息队列等。

常用命令

redis-cli:命令行中开启redis
flushdb:刷新库,库里内容删除
字符串类型
set test:count 1
get test:count
incr test:count 变量加一
decr test:count 变量减一
哈希类型
hset test:user id 1
hset test:user username zhangsan
hget test:user id
list列表类型(横向容器:支持左右进,左右出)
lpush test:ids 101 102 103 左边进
llen test:ids 列表的长度
lindex test:ids 0 看索引为0的值
lrange test:ids 0 2 看索引为0到2的值
rpop test:ids 从右边弹出一个值
set集合类型(无序,不重复)
sadd test:students aaa bbb ccc 向集合添加元素
scard test:students 统计集合中有多少元素
spop test:students 从集合中随机弹出元素 (eg:抽奖)
smember test:students 统计集合中还有多少元素

sortedset 有序集合(提供按分数排序的功能)
zadd test:students 10 aaa 20 bbb 30 ccc 40 ddd 向集合添加元素,(10,20,30是分数)
zcard test:students 统计集合中有多少元素
zscore test:students ccc 查ccc的分数
zrank test:students ccc 查ccc的排名(从小到大,0开始)
zrange test:students 0 2 从小到大排序,取0-2范围的数

全局的命令,对所有的数据类型都有效
keys * 查库里key有什么
keys test * 查库里以test开头的可以有什么
type test:user 看这个key的值的类型
exists test:user 看这个key是否存在
del test:user 删掉这个key
expire test:user 10 设置key的过期时间,单位秒

Spring整合Redis

  • 引入依赖
  • 配置Redis
    配置数据库参数
    编写配置类,构造RedisTemplate(Spring Boot自动实现的key是object类型,变成String类型)
  • 访问Redis
    RedisTemplate.opsForValue()
    RedisTemplate.opsForHash()
    RedisTemplate.opsForList()
    RedisTemplate.opsForSet()
    RedisTemplate.opsForzSet()
// 编程式事务
    // 把操作放到一个队列中去,提交时统一提交给redis,统一处理
    @Test
    public void testTransactional() {
        Object obj = redisTemplate.execute(new SessionCallback() {
            @Override
            // operations执行命令对象,这个方法调用时会把执行命令对象传进来,用其去管理事务
            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();  //提交事务
            }
        });

在这里插入图片描述

点赞

  • 点赞 (异步请求)
    支持对帖子、评论点赞
    第一次点赞,第二次点取消点赞
  • 首页点赞数量
    统计帖子的点赞数量
  • 详情页点赞数量
    统计点赞数量
    显示点赞状态

Redis根据key进行数据的存储和获得,指定key的格式,有利于查询和存储

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";
    private static final String PREFIX_FOLLOWEE = "followee";
    private static final String PREFIX_FOLLOWER = "follower";
    private static final String PREFIX_KAPTCHA = "kaptcha";
    private static final String PREFIX_TICKET = "ticket";
    private static final String PREFIX_USER = "user";

    // 某个实体的赞
    // like:entity:entityType:entityId -> set(userId)
    public static String getEntityLikeKey(int entityType, int entityId) {
        return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId;
    }
    // 某个用户的赞
    // like:user:userId -> int
    public static String getUserLikeKey(int userId) {
        return PREFIX_USER_LIKE + SPLIT + userId;
    }
    // 某个用户关注的实体
    // 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;
    }
    // 登录验证码
    public static String getKaptchaKey(String owner) {
        return PREFIX_KAPTCHA + SPLIT + owner;
    }
    // 登录的凭证
    public static String getTicketKey(String ticket) {
        return PREFIX_TICKET + SPLIT + ticket;
    }
    // 用户
    public static String getUserKey(int userId) {
        return PREFIX_USER + SPLIT + userId;

@Service
public class LikeService {

    @Autowired
    private RedisTemplate redisTemplate;

    // 点赞
    // 事务管理,点赞状态和用户收到的赞的总改变要同时进行
    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);
                String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId);

                //userid判断是否已经在entityLikeKey对应的set中了
                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);
                    operations.opsForValue().increment(userLikeKey);
                }

                return operations.exec();
            }
        });
    }
    // 查询某实体点赞的数量
    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;
    }
    // 查询某个用户获得的赞
    public int findUserLikeCount(int userId) {
        String userLikeKey = RedisKeyUtil.getUserLikeKey(userId);
        Integer count = (Integer) redisTemplate.opsForValue().get(userLikeKey);
        return count == null ? 0 : count.intValue();
    }
    @LoginRequired  //自定义注解,不登录不能点赞
    @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); //异步请求
    }

关注、取消关注

  • 需求
    开发关注、取消关注功能。
    统计用户的关注数、粉丝数
  • 关键
    若A关注了B,则A是B的Follower(粉丝),B是A的Followee(目标)
    关注的目标可以是用户、帖子、题目等,在实现时将这些目标抽象为实体
@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
// 个人主页
    @LoginRequired
    @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";
    }

关注列表、粉丝列表

  • 业务层
    查询某个用户关注的人, 支持分页。
    查询某个用户的粉丝,支持分页
  • 表现层
    处理“查询关注的人”,“查询粉丝”请求
    编写“查询关注的人”,“查询粉丝”模板

关注列表查询对方关注的用户,并将其user,关注时间,以及我是否关注这几个信息返回
在这里插入图片描述

@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());
        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";
    }

    @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);
    }

优化登录模块

  • 使用Redis存储验证码
    验证码需要频繁的访问和刷新,对性能要求较高
    验证码不需要永久保存,在较短时间内会失效
    分布式部署Session共享问题
  • 使用Redis存储登录凭证
    处理每次请求时,都要查询用户的登录凭证,访问频率高
  • 使用Redis缓存用户信息
    处理每次请求时,都要根据凭证查询用户信息,访问频率高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值