使用ORM时如何确保域完整性



除了声明列类型之外,还应如何强制实施域完整性?

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这是使用其他表时不需要

的。

相关内容

  • 没有找到相关文章

最新更新