抽象一个业务:
我们有一张任务表(ts_mission),任务的完成状态(mission_status)有两种类型——
init(未处理)
finished(已完成)
我们需要在数据库中存储状态的类型,还需要读取状态,以及存储状态。
作为一个简单粗暴的程序员,甭给老夫说什么底层、架构,老夫敲代码就是一梭子,Ctrl + C,Ctrl + V,拿起就是干。
额,不好意思,走远了~
我们最简单快捷的做法就是直接在数据库表中创建"mission_status"这个状态,然后根据任务是否完成存储"init"或者"finished",来标志他是"未处理",还是"已完成"。
当一个系统比较小,业务量不复杂的时候,这么做确实可以加快效率,能快速完成我们需要的功能。
可是当数据量上升时,我们就会很难去维护管理这些个状态,一个任务状态还轻松,当有成百上千的状态躺在数据库表中,就非常不便于管理。
为了知晓这个"init"是什么意思,就得翻看注视或者代码去看这个状态到底是什么意思。
ps:当然,有直接将字段状态存储为中文的做法,但是业界不这么做,毕竟存储英文不会因为编码造成读取错误。
然后,我们在做状态查询的时候,读取到的就是一个一个的英文状态,不能知道是什么意思。
通用的能想到的解决方式就是:
SELECT
A.id,
A.mission_name missionName,
A.mission_status missionStatus,
CASE A.mission_status
WHEN 'init' THEN '未处理'
WHEN 'finished' THEN '已完成'
ELSE ''
END missionStatusStr
FROM ts_mission A
使用case when的方式,对返回的状态信息进行判断整理,最后返回。
这样做是没问题的,而且以我个人的编程经验来看:
不存在最好的程序,只存在符合需求的程序。
写代码也一如制造钢琴,能生产出符合需要的程序,就已经足够了。入门级别的钢琴能在2W左右买到,稍好点的,上个4-5W,也非常厉害了,但是想要达到一种完美的状态,给个100W都不够的。所以,一个程序完善了大部分的漏洞,能够在线上稳定运行就可以了。
所谓优化,也是当业务量剧增,你不得不考量成本的时候,再去做的。
上述的状态查询方式,给予了数据库额外的计算压力,而且直接将业务逻辑写在查询语句中,也是不便于后期管理维护的。
所以,至此,我提出了两个问题:
1、如何快捷方便地管理存储在行数据中的状态信息?
2、如何快捷方便地查询存储在数据行中的状态信息?
各位要坚信,我们做程序的都是一表人才。
一表人才:不管遇到什么问题,都能通过增加一张表来解决问题的人才。
所以,我们新建一张用于存储各类状态信息的表(譬如ts_dict_data),主要存储字段状态的值(data_value),以及这个值对应的描述(data_description,也就是这个值表示什么意思),再为这张表添加一个data_code的字段,对应其他表需要存储状态的字段名称(譬如ts_mission表的mission_status),建立与ts_dict_data表的连接关系。
将所有的状态信息数据集中化管理起来,并且这样操作不会影响到以前的表设计。
上述的方法把各个状态都抽离出来,做了集中化处理,需要添加、修改、删除什么状态,都可以在一个位置搞定。
在做web端或者app端的时候,查询某个状态的各种状态,也可以根据上述的data_code查询到对应的数据,做相应的数据操作。
那接着解决上文提及的查询问题。
在做稍微大型点的项目的时候,我们不能直接把查询的业务逻辑写在SQL语句里的。
我们对应ts_mission表的mission_status,创建一个MissionStatus的枚举类(这里针对的java语言):
package com.biz.primus.base.enums;
import com.biz.primus.common.enums.EnumerableValue;
/**
* 任务完成状态
* @author spirit
* @version 1.0
* @date 2018-09-04 17:00
*/
public enum MissionStatus implements EnumerableValue {
/** 任务完成状态为未处理状态 */
init(1,"未处理"),
/** 任务完成状态为已完成状态 */
finished(2,"已完成");
/** 枚举对应数值 */
private int value;
/** 描述 */
private String description;
MissionStatus(int value, String description){
this.value = value;
this.description = description;
}
@Override
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
接着创建TsMissionVo传输类,并且在这个类里面使用我们创建的枚举类型:
package com.biz.primus.base.enums;
import lombok.Data;
import java.io.Serializable;
/**
* 任务传输类
* @author spirit
* @version 1.0
* @date 2018-09-04 16:55
*/
@Data
public class TsMissionVo implements Serializable {
/** 主键ID */
private Long id;
/** 任务名称 */
private String missionName;
/** 任务状态 */
private MissionStatus missionStatus;
/** 任务状态描述 */
private String missionStatusStr;
public void setMissionStatus(MissionStatus status) {
this.missionStatus = status;
if(status != null){
this.missionStatusStr = status.getDescription();
}
}
}
最后,当我们在使用MyBatis做查询的时候,就不用再做前文的查询方式了(TsMissionVo是MyBatis的resultType的结果集):
SELECT
A.id,
A.mission_name missionName,
A.mission_status missionStatus
FROM ts_mission A
以上是最近在工作中悟出的一些小感想。
最后,大家有什么不懂的或者其他需要交流的内容,也可以进入我的QQ讨论群一起讨论:654331206