除了声明列类型之外,还应如何强制实施域完整性?
ENUM()
是一种选择,但是,从SQL角度和Doctrine的角度来看,它都有缺点。
作为替代方案,在使用ORM之前设计数据库时,我会使用自然键和外部约束。 虽然我的time_unit
表有一个名称和秒列,但它唯一真正的意义是将其他表中的值限制为time_unit.unit
的值。
MariaDB [tracker]> select * from time_unit;
+------+-----------+----------+
| unit | name | seconds |
+------+-----------+----------+
| d | Days | 86400 |
| h | Hours | 3600 |
| i | Minutes | 60 |
| m | Month | 2592000 |
| q | Quarter | 10368000 |
| s | Seconds | 1 |
| w | Week | 604800 |
| y | Year | 31536000 |
+------+-----------+----------+
8 rows in set (0.01 sec)
MariaDB [tracker]> select * from sign;
+------+
| sign |
+------+
| -1 |
| 1 |
+------+
2 rows in set (0.00 sec)
CREATE TABLE `agenda` (
`id` int(11) NOT NULL,
`time_value` smallint(6) DEFAULT NULL,
`time_unit` varchar(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`sign` smallint(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_agenda_time_unit1_idx` (`time_unit`),
KEY `fk_agenda_sign1_idx` (`sign`),
CONSTRAINT `FK_5A9C89CF7106057E` FOREIGN KEY (`time_unit`) REFERENCES `time_unit` (`unit`),
CONSTRAINT `FK_22ACC67D9F7E91FE` FOREIGN KEY (`sign`) REFERENCES `sign` (`sign`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
但是在使用ORM时,这有问题。 首先,为每个对象创建一个对象,这似乎过多,并使序列化变得痛苦。 其次,我已经读到,在使用ORM时,不应该使用外键,而应该使用关联的对象。
在数据库级别,表示实体之间的关系 通过外键。但是有了教义,你就永远不必(也永远不会 应该)直接使用外键。你应该只使用 通过自己的标识表示外键的对象。
为了强制实施域完整性,应该使用 ENUM()、对自然键的外部约束还是其他方法?
这主要是风格问题。构建架构时,引用完整性是保证质量的一种方式。还有其他方法可以做到这一点 - 例如,单元测试保证系统不会接受 1 和 -1 以外的"符号"值。
因此,如果你有一个仅用于限制有效条目的表,如果没有其他属性,我建议做最简单的事情,并将该逻辑放在你的应用程序层中,使用单元测试来验证你想要的行为。只需将数据库中的数据存储为没有外键的整数即可。
如果你真的想让数据库验证条目,你可以使用枚举,或者告诉ORM列只是一个整数,但仍然创建外键。这是否是一个好主意再次取决于风格。我非常喜欢"干"——不要重复自己。如果您有逻辑来限制应用程序层中某个属性的有效选项,请专注于使其更好,不要在数据库架构中重复该逻辑。不过,这并不是那么简单和干燥 - 如果您认为人们会在您的应用程序之外访问数据库,那么拥有外键或枚举是合法的。
我认为您的time_unit
不仅仅是一组查找值 - 我猜"秒"列用于在单位之间进行转换。这里有几个选项,但同样,我想依靠单元测试来验证我的转换逻辑,如果这是转换逻辑发生的地方,您可以考虑将这些常量存储到应用程序代码中。然后,您可以将单元存储为议程表中的字符列。
这使得持久性逻辑更容易、更快捷,但将验证行为的责任放在单元测试中,而不是架构中。
我认为关于从不直接使用外键的引用旨在表明使用 ORM 的"正常"行为是通过请求议程对象 (print agendaItem.time_unit.name
) 来访问time_unit,而不是显式请求外键并检索相关对象 (timeUnitID = agendaItem.timeUnitID; print time_unit.findByID(timeUnitID
)。我不认为这是针对外键的全面建议。
ENUM() 是一个选项,但是,从 SQL 的角度来看,它有缺点 以及教义的观点。
要强制实施域完整性,应 ENUM(),外部约束 自然键,还是使用其他方法?
您可以替换MySQL的/MariaDB的数据类型
ENUM('1', '2', '3');
用更理性的方法。
CREATE TABLE enum_data (
position TINYINT
, value CHAR(1)
);
INSERT INTO enum_data (position, value) VALUES(1, '1');
INSERT INTO enum_data (position, value) VALUES(2, '2');
INSERT INTO enum_data (position, value) VALUES(3, '3');
并在您的表格中使用它
CREATE TABLE your_table (
id INT ....
, enum_data_id TINYINT
...
...
)
您还可以/应该使用FOREIGN KEY
来确保数据完整性。
使用额外的表也比ENUM
具有优势。
当您要从ENUM
数据类型中添加、编辑、删除时,您必须需要使用ALTER TABLE
这是使用其他表时不需要