数据标签场景的设计方案以及BiConsumer的妙用

1,420 阅读4分钟

[toc]

伴随着互联网的发展,数据标签不知从何时应运而生。譬如各种社交APP,个人中心都有标签功能,言简意赅,对某人的文字概述。诚然,数据标签也可以用到日常运营平台中,其作用可以作为归类,检索作用。如果发挥的好,就像自己整一个书架一样,瞬间找到文学类,计算机类,科学类书籍。这就得看你是否灵活运用了,当然下面案例就是讲究如何设计。

1、需求场景

假设现在我们有多个模块,然后我们想通过对多个功能模块的查询列表增加数据标签,以便数据归类和检索功能,这时候我们怎么设计呢?

比如说现在我们有学生模块、老师模块、课程模块,然后实现各个模块的检索和分类。

在这里插入图片描述

2、方案设计

2.1 关系型DB存储

2.1.1 表结构设计

如果我们采用关系型DB,譬如MySQL存储,可以参考的表结构如下

CREATE TABLE `tags` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `buz_type` tinyint(4) NOT NULL COMMENT '业务类型(1:学生 2:老师 3:课程)',
  `source_id` int(11) NOT NULL COMMENT '关联id',
  `tags` varchar(4000) DEFAULT NULL COMMENT '标签,多个以,分割',
  PRIMARY KEY (`id`),
  UNIQUE KEY `udx_tags` (`buz_type`,`source_id`)
) ENGINE=InnoDB COMMENT='标签表';

如上,我们通过一个中间表,其中buz_type表示业务类型,source_id表示关联ID、tags为标签字段。这样设计的好处,可以通过buz_type增加业务类型支持更多模块。

2.1.2 java代码示例

以下代码为伪代码,示例代码如下

学生类、老师类、标签类

/**
 * @description: 学生
 * @Date : 2020/4/24 下午7:40
 * @Author : 石冬冬-Seig Heil
 */
@Data
public class Student implements Serializable {
    /**
     * id
     */
    private Integer id;
    /**
     * 学生标签
     */
    private String name;
    /**
     * 标签
     */
    private List<String> tags;
}

/**
 * @description: 老师
 * @Date : 2020/4/24 下午7:40
 * @Author : 石冬冬-Seig Heil
 */
@Data
public class Teacher implements Serializable {
    /**
     * id
     */
    private Integer id;
    /**
     * 学生标签
     */
    private String name;
    /**
     * 标签
     */
    private List<String> tags;
}


/**
 * @description: 标签
 * @Date : 2020/4/24 下午9:14
 * @Author : 石冬冬-Seig Heil
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Tag implements Serializable {

    private Integer id;

    private Integer buzType;

    private Integer sourceId;

    private String tags;
}

/**
 * @description: 标签查询参数对象
 * @Date : 2020/4/24 下午9:17
 * @Author : 石冬冬-Seig Heil
 */
@Data
public class TagParam {

    private Integer buzType;

    private Integer sourceId;
}

Service类

/**
 * @description: 标签类
 * @Date : 2020/4/24 下午9:16
 * @Author : 石冬冬-Seig Heil
 */
public class TagService {

    public List<Tag> queryTags(TagParam param) {
        //查询数据,这里是伪代码
        return new ArrayList<>();
    }
}


/**
 * @description: 学生类
 * @Date : 2020/4/24 下午9:27
 * @Author : 石冬冬-Seig Heil
 */
public class StudentService {
    private TagComponent tagComponent;
    public List<Student> queryList() {

        // 1、假设这里查到数据
        List<Student>  resultList = new ArrayList<>();
        // 2、对数据景先处理
        tagComponent.bindTags(resultList,1,each -> each.getId(),(each,tags) -> each.setTags(tags));
        return resultList;
    }
}


/**
 * @description: 老师类
 * @Date : 2020/4/24 下午9:27
 * @Author : 石冬冬-Seig Heil
 */
public class TeacherService {
    
    private TagComponent tagComponent;

    public List<Teacher> queryList() {

        // 1、假设这里查到数据
        List<Teacher>  resultList = new ArrayList<>();
        // 2、对数据景先处理
        tagComponent.bindTags(resultList,2,each -> each.getId(),(each,tags) -> each.setTags(tags));
        return resultList;
    }
}

Service类

/**
 * @description: 标签组件类
 * @Date : 2020/4/24 下午9:21
 * @Author : 石冬冬-Seig Heil
 */
@Slf4j
public class TagComponent {


    private TagService tagService;

    /**
     * 绑定标签,巧用Function<T,R> 和 BiConsumer<T,U>两个函数
     * @param list 数据元素
     * @param buzType 业务类型
     * @param function Function<T,R>
     * @param consumer BiConsumer<T,U>
     * @param <T> 集合元素
     * @param <R> 集合元素属性
     */
    public  <T,R>  void bindTags(List<T> list,Integer buzType,Function<T,R> function, BiConsumer<T,List<String>> consumer){
        try {
            //1、构建查询参数,只需要业务类型集合
            TagParam form = new TagParam();
            form.setBuzType(buzType);
            //2、查询对应业务类型标签
            List<Tag> tagList = tagService.queryTags(form);
            if(!tagList.isEmpty()){
                //3、构建Map,key=业务id,value=标签
                Map<Integer,String> tagMap = tagList.stream().collect(Collectors.toMap(Tag::getSourceId,Tag::getTags));
                list.forEach(rule -> {
                    //4、获取对应业务id的标签
                    String tags = tagMap.get(function.apply(rule));
                    if(null != tags && tags.length() > 0){
                        //5、回调处理,把数据标签集合以及集合元素给调用处理
                        consumer.accept(rule, Arrays.asList(tags.split(",")));
                    }
                });
            }
        } catch (Exception e) {
            log.error("设置标签异常,tuple={}",e);
        }
    }
}

2.2 结构化DB存储

如果选型 Redis,这里我们可以使用K-V数据存储即可。

代码嘛,就不用写了。简单思路如下

key=关联id
value=集合,json数组即可
set(key,value)

我们可以使用基于Redis缓存+MySQL混合式使用,减少对数据库的频繁查询,毕竟标签也是读多写少的应用场景。一旦选择此方案,我们想到数据的一致性,或者基于Canal实现,脱离业务逻辑处理以解耦。

友情链接:

blog.csdn.net/shichen2010…

下面的是我的公众号二维码图片,欢迎关注。

在这里插入图片描述