【发布时间】:2009-04-17 16:46:38
【问题描述】:
如何在不支持枚举的数据库中实现枚举字段? (即 SQLite)
需要使用“field = ?”轻松搜索这些字段所以使用任何类型的数据序列化都是一个坏主意。
【问题讨论】:
-
如果这是重复的,请善待。在我问之前,我确实在 StackOverflow 上寻找过答案。
标签: database-design enums
如何在不支持枚举的数据库中实现枚举字段? (即 SQLite)
需要使用“field = ?”轻松搜索这些字段所以使用任何类型的数据序列化都是一个坏主意。
【问题讨论】:
标签: database-design enums
对查找表使用外键是我使用的方法。事实上,即使我使用支持 ENUM 的数据库(例如 MySQL),我也会使用它。
为简单起见,我可能会跳过查找表中一直存在的“id”,而只使用主表中我需要的实际值作为查找表的主键。这样您就不需要进行连接来获取值。
CREATE TABLE BugStatus (
status VARCHAR(20) PRIMARY KEY
);
INSERT INTO BugStatus (status) VALUES ('NEW'), ('OPEN'), ('FIXED');
CREATE TABLE Bugs (
bug_id SERIAL PRIMARY KEY,
summary VARCHAR(80),
...
status VARCHAR(20) NOT NULL DEFAULT 'NEW',
FOREIGN KEY (status) REFERENCES BugStatus(status)
);
诚然,存储字符串比 MySQL 的 ENUM 实现占用更多空间,但除非相关表有数百万行,否则几乎没有关系。
查找表的其他优点是您可以使用简单的INSERT 或DELETE 在列表中添加或删除值,而使用ENUM 您必须使用ALTER TABLE 重新定义列表。
还可以尝试在ENUM 中查询当前允许值列表,例如在您的用户界面中填充选择列表。这是一个很大的烦恼!使用查找表,很容易:SELECT status from BugStatus。
如果需要,您还可以将其他属性列添加到查找表中(例如,标记仅对管理员可用的选项)。在ENUM 中,您不能注释条目;它们只是简单的值。
除了查找表之外的另一个选项是使用 CHECK 约束(前提是数据库支持它们——MySQL 直到版本 8.0.16 才支持 CHECK):
CREATE TABLE Bugs (
bug_id SERIAL PRIMARY KEY,
summary VARCHAR(80),
...
status VARCHAR(20) NOT NULL
CHECK (status IN ('NEW', 'OPEN', 'FIXED'))
);
但是CHECK 约束的这种使用具有与ENUM 相同的缺点:没有ALTER TABLE 难以更改值列表,难以查询允许值列表,难以注释值。
PS:SQL 中的相等比较运算符是单个=。双 == 在 SQL 中没有任何意义。
【讨论】:
为了限制可能的值,我将对包含枚举项的表使用外键。
如果您不想 JOIN 进行搜索,则将键设为 varchar 如果 JOINS 没有问题,然后将键设为 INT 并且不要加入,除非您需要在该字段上进行搜索。
请注意,将您的枚举放在数据库中会排除在编译时检查代码中的值(除非您在代码中重复枚举。)我发现这是一个很大的缺点。
【讨论】:
你基本上有两种选择:
使用整数字段
使用 varchar 字段
我个人会提倡使用 varchars,因为如果你改变你的枚举,你不会破坏任何东西 + 字段是人类可读的,但是 int 也有一些优点,即性能(数据的大小是显而易见的例子)
【讨论】:
这是我最近做的
在我的休眠映射 POJO 中,我将成员的类型保持为 String,它在数据库中是 VARCHAR。
这个设置器需要一个枚举 还有另一个 setter 接受 String- 但这是私有的(或者您可以直接映射该字段 - 如果您愿意的话。)
现在,我使用 String 的事实已被全部封装。对于应用程序的其余部分——我的域对象使用枚举。 就数据库而言——我使用的是字符串。
如果我错过了您的问题,我深表歉意。
【讨论】:
如果你使用 Spring JPA 2.1 或更高版本和 hibernate,你可以实现你自己的 AttributeConverter 并定义你的枚举如何映射到列值
@Converter(autoApply = true)
public class CategoryConverter implements AttributeConverter<Category, String> {
@Override
public String convertToDatabaseColumn(Category category) {
if (category == null) {
return null;
}
return category.getCode();
}
@Override
public Category convertToEntityAttribute(String code) {
if (code == null) {
return null;
}
return Stream.of(Category.values())
.filter(c -> c.getCode().equals(code))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}
}
请参阅文章Persisting Enums in JPA中的第4个解决方案。代码sn-p也来自那里。
【讨论】:
我会使用 varchar。这不适合您吗?
【讨论】: