浪花 - 根据标签搜索用户

用户中心:集中提供用户的检索、操作、注册、登录、鉴权

此接口与用户关系紧密,在【用户中心】项目后端新增一个接口——根据标签搜索用户

  • AND:允许用户传入多个标签,多个标签都存在才能搜索出来
  • OR:允许用户传入多个标签,有任何一个标签存在就能搜索出来

本文演示两种方式进行标签搜索:

  • 使用 SQL 查询:通过 Mybatis-plus 的 like 方法实现标签信息 tags 的模糊匹配,优点是实现简单
  • 使用内存查询:优点是可以在代码中呈现,使用灵活

方式一:使用 SQL 查询

1. 使用 MybatisX 插件简化开发(在插件商店直接搜索 MybatisX 点击 install 进行插件的安装)

  • 选择要操作的表,自动生成下列文件,提高开发效率

    • 数据库表 - Java 实体类映射文件:Tag.java

    • 针对 tag 表的数据库业务层操作:TagService.java 和 TagServiceImpl.java

    • 针对 tag 表的数据库持久层操作:TagMapper.java

    • 数据库操作映射 SQL(较复杂的 SQL 语句)文件:TagMapper.xml

浪花 - 根据标签搜索用户_第1张图片

2. 编写模糊查询匹配用户标签的业务代码

    /**
     * 根据标签搜索用户
     * @param tagNameList 用户的标签列表
     * @return 匹配该标签列表的用户列表
     */
    public List searchUsersByTags(List tagNameList) {
        // 1. 判断参数是否为空
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 2. 构造查询器
        QueryWrapper queryWrapper = new QueryWrapper();
        for (String tagName : tagNameList) {
            queryWrapper = queryWrapper.like("tags", tagName);
        }

        // 3. 根据标签列表查找用户
        List userList = userMapper.selectList(queryWrapper);

        // 4. 返回脱敏后的用户列表
        return userList.stream().map(this::getSafetyUser).collect(Collectors.toList());
    }

3. 开启 Mybatis-plus 的 SQL 语句日志输出:在项目配置文件 application.yml 中添加下列配置信息

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志

浪花 - 根据标签搜索用户_第2张图片

 4. Junit 单元测试

@Test
public void testSearchUsersByTags() {
    List tagNameList = new ArrayList<>();
    tagNameList.add("java");
    tagNameList.add("python");
    List userList = userService.searchUsersByTags(tagNameList);
    Assertions.assertNotNull(userList);
}

浪花 - 根据标签搜索用户_第3张图片

方式二:使用内存查询

使用 SQL 全量查询出所有用户,在内存中计算过滤符合条件的用户

1. 先查询所有用户,遍历用户并取出用户的标签信息

2. 在内存中判断 tags 是否包含所要求的标签

  • 引入 Gson 依赖:将 tags 序列化为 Java 对象,使用 Set 集合接收
  • 遍历要搜索的标签列表,判断用户是否拥有该标签
    /**
     * 根据标签搜索用户
     * @param tagNameList 用户的标签列表
     * @return 匹配该标签列表的用户列表
     */
    public List searchUsersByTags(List tagNameList) {
        // 1. 判断参数是否为空
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }


        /**
         * 方式二:使用内存查询
         */
        // 1. 查询所有用户
        QueryWrapper queryWrapper = new QueryWrapper();
        List userList = userMapper.selectList(queryWrapper);

        Gson gson = new Gson();
        return userList.stream().filter(user -> {
            // 2. 遍历用户取出标签信息
            String tagsStr = user.getTags();
            if (StringUtils.isEmpty(tagsStr)) {
                return false;
            }
            // 3. 在内存中判断是否包含要求标签
            Set tempTagsNameSet = gson.fromJson(tagsStr, new TypeToken>(){}.getType());
            for (String tagName : tagNameList) {
                if (!tempTagsNameSet.contains(tagName)) {// 用户标签不包含所要求的标签
                    return false;
                }
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
    }

3. Junit 单元测试

浪花 - 根据标签搜索用户_第4张图片

4. 判断两种方式的效率

  • 通过实际测试(计算)两种方法的运行时间来分析
  • 数据量大时效果更明显

5. 思路扩展:如果两种查询不相上下,时而 SQL 查询快,时而内存查询快,如何选择?

  • 如果参数可以分析,可以根据用户的参数(比如搜索标签的数目)来选择适合的查询方式
  • 如果参数不可分析,且数据库连接足够,内存空间足够,可以并发使用两种方式同时查询,哪种方式先返回数据就用谁的数据
  • SQL 查询与内存运算相结合,先用 SQL 过滤一部分用户:例如要查询包含 "java" 等几十个标签用户,可以先使用 "java" 标签进行过滤,只剩下包含 "java" 标签的用户,再到内存中计算用户的其他标签

序列化和反序列化

1. 序列化和反序列化:将对象转换为字节流以便在网络中进行传输和存储的过程

  • 序列化:将 Java 对象转为 JSON 字符串
  • 反序列化:将 JSON 字符串转为 Java 对象

2. 几种 JSON 序列化库

  • fastjson(ali 出品)
  • gson(google 的,推荐)✔(本文使用方式)
  • jackson
  • kryo
  • hutool 工具中的 JSONUtil 
    • 引入依赖
    • 序列化:JSONUtil.toBean(json, Object.class); 这里不适用,需要指定一个 Java 对象的字节码类型,但是我们的 tags 不是一个 Java 对象
    • 反序列化:JSONUtil.toJsonStr(obj);

你可能感兴趣的:(#,后端接口开发,java,junit,sql,redis,mybatis,json,intellij,idea)