关于状态字段和类似的预定义值集合,存在一个经常性的问题。
我们以订单系统为例,订单实体有一个状态字段,可能是“新建”、“进行中”、“已支付”等。
问题:
订单状态需要:
- 存储(在数据库中)
- 处理(在后端处理)
- 传达(通过Web服务API向前端通信)
如何在保留以下条件的情况下执行这三个活动:
- 保留状态的含义。
- 高效的存储。
以下是一些带有其优缺点的示例实现:
1- 状态表
- 数据库将包含一个id、名称的状态表
订单表引用状态的id。
CREATE TABLE `status` (
`id` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `order` (
`id` INT NOT NULL AUTOINCREMENT,
`status_id` INT NOT NULL,
PRIMARY KEY (`id`),
INDEX `order_status_idx` (`status` ASC),
CONSTRAINT `order_status_id`
FOREIGN KEY (`status_id`)
REFERENCES `status` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
后端代码有一个枚举,为这些预定义的整数在代码中赋予了意义。enum Status {
PAID = 7;
};
// While processing as action ...
order.status = Status::PAID;
网络服务 API 将返回状态码
order: { id: 1, status_id: 7 }
前端代码有一个类似的枚举,给这些预定义的整数在代码中赋予了意义。(与后端代码类似)
优点:
- 数据库定义良好且规范化
- 状态编号和意义之间的映射在三个地方完成,这给人类造成了错误和不一致性,难以定义特定状态编号的含义。
- API返回的数据不具备描述性,因为
status_id:7
并没有传递出具体的含义,因为它不包括status_id:7
的含义。
2- 状态枚举
在数据库中,订单表将包含一个类型为ENUM的状态列,其中包含预定义的状态。
CREATE TABLE IF NOT EXISTS `order` (
`id` INT NOT NULL AUTOINCREMENT,
`status` ENUM('PAID') NULL,
PRIMARY KEY (`id`));
后端代码将预定义状态设置为代码工件的常量值
enum Status {
PAID = 'PAID'
};
class Status {
public:
static const string PAID = PAID;
};
请按以下方式使用
// While processing as action ...
order.status = Status::PAID;
网络服务 API 将返回状态常量
order: { id: 1, status: 'PAID' }
The frontend code will have a similar construct for predefined status constants, as in the backend code.
Pros:
- The database is well-defined and normalized, with a separate status table that allows for easier modifications without expensive ALTER commands.
- The returned data from the API is descriptive and delivers the required meaning.
- The status constants used already contain their meaning, which reduces the chances of errors.
- None mentioned.
3- My proposed solution:
The database will contain a status table with one field called
key
with type string, which is the primary key of this table.
CREATE TABLE `status` (
`key` VARCHAR(45) NOT NULL,
PRIMARY KEY (`key`));
订单表将包含名为status
的字段,类型为字符串,该字段引用status
表的key
字段。
CREATE TABLE IF NOT EXISTS `order` (
`id` INT NOT NULL AUTOINCREMENT,
`status` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`),
INDEX `order_status_idx` (`status` ASC),
CONSTRAINT `order_status`
FOREIGN KEY (`status`)
REFERENCES `status` (`key`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
后端代码将预定义状态的常数值作为代码构件
enum Status {
PAID = 'PAID'
};
class Status {
public:
static const string PAID = PAID;
};
应按以下方式使用
// While processing as action ...
order.status = Status::PAID;
网络服务API将返回状态常量。order: { id: 1, status: 'PAID' }
前端代码将具有类似于预定义状态常量的结构。(与后端代码类似)
优点:
- 数据库已经定义并规范化
- 从API返回的数据是描述性的,并提供所需的含义。
- 使用的状态常量已经包含它们的含义,从而减少了出错的可能性。
- 通过在状态表中使用INSERT命令添加新的状态常量非常简单。
- ???
我想知道这是否是可行的解决方案,或者是否存在更好的解决方案来解决这个反复出现的问题。
请说明为什么所提出的解决方案不好,并且您的更好解决方案为何更好。
谢谢。
{status:'PAID'}
而不是假设API应该公开数据库结构,这只是没有完成的简单操作,在代码中只需返回与关系匹配的字符串值的状态清晰属性对我来说性能将受到影响,因为需要从状态表中加载相关行。 - Abdelrahman M. Allam