1 需求背景

在系统中用户一共有三种角色:普通用户,管理员,超级管理员,现在需要设计一张用户角色表记录这类信息。我们不难设计出如下方案。

id name super admin normal
101 用户一 1 0 0
102 用户二 0 1 0
103 用户三 0 0 1
104 用户四 1 1 1

我们使用1表示是,0表示否,那么观察上表不难得出,用户一有用超级管理员角色,用户二具有管理员角色,用户三具有普通用户角色,用户四同时具有三种角色。

如果此时新增加一种角色应该如何设计?其实新增一个字段即可解决。

id name super admin normal new
101 用户一 1 0 0 0
102 用户二 0 1 0 0
103 用户三 0 0 1 0
104 用户四 1 1 1 1

2 发现问题

按照上述做法进行表设计功能上是没有问题的,优点是容易理解结构清晰,但是我们想一想有没有什么问题?笔者遇到过如下问题:

在复杂业务环境一份数据可能会使用在不同的场景,例如上述数据存储在MySQL数据库,这一份数据还会在如下场景使用。

  • 检索数据需要同步一份到ES

  • 业务方使用此表通过Flink计算业务指标

  • 业务方订阅此表Binlog消息进行业务处理

如果表结构发生变化,数据源之间就要重新进行对接,业务方也要进行代码修改,这样开发成本非常高。有没有办法避免此类问题?

3 解决方案

我们可以使用位图法,这样同一个字段可以表示多个含义。首先设计如下数据表,userFlag字段先不填。

id name user_flag
101 用户一
102 用户二
103 用户三
104 用户四

我们使用位图法每一个bit表示一种角色

我们使用位图法表示如下数据表

id name super admin normal
101 用户一 1 0 0
102 用户二 0 1 0
103 用户三 0 0 1
104 用户四 1 1 1

用户一位图如下转换为十进制数值是4

用户二位图如下转换为十进制数值是2

用户三位图如下转换为十进制数值是1

用户四位图如下转换为十进制数值是7

根据分析我们可以填写完整数据表

id name user_flag
101 用户一 4
102 用户二 2
103 用户三 1
104 用户四 7

4 位图法详解

本章节我们分析位图法方案一些关键节点。

4.1 枚举定义

定义枚举时不要直接定义为1、2、4这类数字,而是采用位移方式定义,这样使用者可以明白设计者意图。

/*** 用户角色枚举** @author 公众号JAVA前线**/
public enum UserRoleEnum {// 1 -> 00000001NORMAL(1, "普通用户"),// 2 -> 00000010MANAGER(1 << 1, "管理员"),// 4 -> 00000100SUPER(1 << 2, "超级管理员");private int code;private String description;private UserRoleEnum(Integer code, String description) {this.code = code;this.description = description;}public String getDescription() {return description;}public int getCode() {return this.code;}
}

4.2 维护角色

假设用户已经具有普通用户角色,我们需要为其增加管理员角色,这就是新增角色,与之对应还有删除角色和查询角色,这些操作需要用到位运算,说明详见代码注释。

/*** 用户角色枚举** @author 公众号JAVA前线**/
public enum UserRoleEnum {// 1 -> 00000001NORMAL(1, "普通用户"),// 2 -> 00000010MANAGER(1 << 1, "管理员"),// 4 -> 00000100SUPER(1 << 2, "超级管理员");// 新增角色 -> 位或操作// oldRole -> 00000001 -> 普通用户// addRole -> 00000010 -> 新增管理员// newRole -> 00000011 -> 普通用户和管理员public static Integer addRole(Integer oldRole, Integer addRole) {return oldRole | addRole;}// 删除角色 -> 位异或操作// oldRole -> 00000011 -> 普通用户和管理员// delRole -> 00000010 -> 删除管理员// newRole -> 00000001 -> 普通用户public static Integer removeRole(Integer oldRole, Integer delRole) {return oldRole ^ delRole;}// 是否有某种角色 -> 位与操作// allRole -> 00000011 -> 普通用户和管理员// qryRole -> 00000001 -> 是否有管理员角色// resRole -> 00000001 -> 有普通用户角色public static boolean hasRole(Integer role, Integer queryRole) {return queryRole == (role & queryRole);}private int code;private String description;private UserRoleEnum(Integer code, String description) {this.code = code;this.description = description;}public String getDescription() {return description;}public int getCode() {return this.code;}public static void main(String[] args) {System.out.println(addRole(1, 2));System.out.println(removeRole(3, 1));System.out.println(hasRole(3, 1));}
}

4.3 数据查询

假设在运营后台查询界面,查询具有普通用户角色的用户数据,我们可以使用SQL语句如下

select * from user_role where (user_flag & 1) = user_flag;
select * from user_role where (user_flag & b'0001') = user_flag;

使用MyBatis语句如下

<select id="selectByUserRole" resultMap="BaseResultMap" parameterType="java.util.Map">select * from user_role where <![CDATA[ user_flag & #{userFlag} = #{userFlag} ]]>
</select><select id="selectByUserIdAndRole" resultMap="BaseResultMap" parameterType="java.util.Map">select * from user_role where id = #{userId} <![CDATA[ and user_flag & #{userFlag} = #{userFlag} ]]>
</select>

5 文章总结

本文我们从一个简单案例开始,分析了直接新增字段的优缺点。使用新增字段笔者遇到最多的问题就是在复杂业务场景中,需要新增数据对接工作量,增加了开发维护成本。

我们又介绍了位图法,这样一个字段就可以表示多种业务含义,减少了字段冗余,节省了对接开发成本。当然位图法也有缺点,增加了代码理解成本,数据库字段含义不直观,需要进行转义,大家可以根据需求场景选择使用。

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

面试官问一个数据表字段怎么表示多种业务含义?我愣了五分钟相关推荐

  1. 疯了疯了!面试官问一个 TCP 连接可以发多少个 HTTP 请求?

    一道经典的面试题是从 URL 在浏览器被被输入到页面展现的过程中发生了什么,大多数回答都是说请求响应之后 DOM 怎么被构建,被绘制出来.但是你有没有想过,收到的 HTML 如果包含几十个图片标签,这 ...

  2. 面试官问百万数据excel导出功能如何实现?

    文章目录 背景 实现 1.异步处理 1.1 使用job 1.2 使用mq 2.使用easyexcel 4.多个sheet 5.计算limit的起始位置 6.文件上传到OSS 7.通过WebSocket ...

  3. 【067期】面试官问:说说常见的加密算法、原理、优缺点及用途?

    >>号外:关注"Java精选"公众号,回复"面试资料",免费领取资料!"Java精选面试题"小程序,3000+ 道面试题在线刷, ...

  4. 面试官问:数据库 delete 表数据,磁盘空间还是被一直占用,为什么?

    以下文章来源方志朋的博客,回复"666"获面试宝典 最近有个上位机获取下位机上报数据的项目,由于上报频率比较频繁且数据量大,导致数据增长过快,磁盘占用多. 为了节约成本,定期进行数 ...

  5. 面试官问我:一个 TCP 连接可以发多少个 HTTP 请求?我竟然回答不上来...

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 作者 | 松若章 来源 | https://zhuanlan.zhihu.com/p/6142 ...

  6. 面试官问:请拿出一段体现你水平的代码,我该如何回答?

    程序员面试,免不了被问代码问题.如果面试官问你,最能代表你的当下水平的代码是什么?你该怎么回答呢?知乎的几位作者给出了优秀答案. 每天下班前半小时都会运行这段. #include <stdlib ...

  7. 面试官问:在读多写少的情况下,如何优化 MySQL 的数据查询方案

    作者 | 面试官问     责编 | 张文 来源 | 面试官问(ID:interviewer_asked) 面试官问:假设你负责的某业务在双十一期间要搞运营活动,公司投入了大量的营销费用进行推广,此举 ...

  8. 【263期】面试官问:假设有一千万数据,怎么快速查询?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 面试 ...

  9. 面试官问:Kafka 会不会丢消息?怎么处理的?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! Kafka存在丢消息的问题,消息丢失会发生在Broker, ...

最新文章

  1. git分支合并(包含学习git命令的方法)
  2. 程序显示文本框_python PDF转成图片小程序
  3. ubuntu更改mysql的编码配置
  4. Lighting System Design UVA - 11400 动态规划
  5. 《剑指offer》求二叉树的最小深度(非递归法)
  6. mysql5.7.18压缩包下载_MySQL 5.7.18 解压版安装
  7. NYOJ---ASCII码排序
  8. ES7 设置磁盘使用率水位线 allocation.disk.watermark
  9. tensorflow之relu
  10. 计算机仿真软件的论文,【计算机仿真论文】计算机仿真软件模拟物流系统研究(共5366字)...
  11. day20/FileDemo1.java
  12. TCPClient代码
  13. aplay与call
  14. 傅里叶变换性质证明卷积_傅里叶变换2.系统属性和卷积公式的推导
  15. 广东又将添新高校:香山大学来了
  16. Harbor docker
  17. ​数字经济指数合集:各省、城市数字经济指数面板数据
  18. 小米手机扩容教程_小米4 16g升级64g教程:小米4 16g扩张64g步骤
  19. Procmon打开后无反应的问题
  20. python 数据分析、挖掘与可视化 day3

热门文章

  1. android百度地图更换定位图标,android百度地图定位,改变MyLocationOverlay默认图标(原始为蓝色点)(两种方法)...
  2. mysql for mac 使用_MySQL for Mac 安装和基本操作
  3. 五年一贯制专转本c语言真题,江苏省五年一贯制专转本《C语言程序设计》模拟试卷二(晓庄)...
  4. HDU2925(约瑟夫环问题)
  5. HDU4720(最小圆覆盖问题)
  6. LCA 朴素算法+树差分倍增+Tarjan算法 三种算法实现c++代码实现
  7. Codeforces Round #649 (Div. 2)C. Ehab and Prefix MEXs[排列的构造]
  8. python 测试linux dev文件,Linux测试开发人员要掌握的Linux命令有哪些?
  9. 化验室计算机用户管理,计算机实验室管理思路及方法
  10. 求数的绝对值一定是正数_「口袋数学」绝对值的几何意义探究及应用,培优课程...