除了声明列类型之外,还应如何强制域完整性?
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时会出现问题。首先,为每个对象创建一个对象,该对象似乎过多并且使序列化变得痛苦。其次,我read表示,在使用ORM时,不应使用外键,而应使用关联的对象。
在数据库级别,表示实体之间的关系 通过外键。但是有了Doctrine,您将永远不必(而且永远 应该)直接使用外键。您应该只与 通过自己的身份表示外键的对象。
要实现域完整性,是否应使用ENUM(),对自然键的外部约束或其他方法?
答案 0 :(得分:1)
这主要是风格问题。在构建架构时,引用完整性是保证质量的一种方法。还有其他方法可以做到这一点-例如,单元测试可确保系统不会接受1和-1以外的“符号”值。
因此,在有一个仅用于限制有效条目的表的情况下,如果没有其他属性,我建议您做尽可能简单的事情,并将该逻辑放入应用程序层,并通过单元测试进行验证您想要的行为。只需将数据作为没有外键的整数存储在数据库中即可。
如果您真的想让数据库验证条目,则可以使用一个枚举,或告诉ORM该列只是一个整数,但仍创建外键。这是否是一个好主意再次取决于风格。我非常喜欢“干”-不要重复自己。如果您有逻辑来限制应用程序层中某个属性的有效选项,那么请集中精力使其更好,不要在数据库模式中重复该逻辑。但这并不是那么简单-如果您认为人们会在您的应用程序外部访问数据库,那么有一个外键或枚举是合理的。
我认为您的time_unit
不仅仅是一组查找值-我猜“秒”列用于在单位之间进行转换。这里有两个选项,但是我还是想依靠单元测试来验证我的转换逻辑,如果发生转换逻辑,则可以考虑将它们作为常量存储在应用程序代码中。然后,您可以将该单元存储为议程表中的char列。
这使持久性逻辑更容易,更快捷,但是将验证行为的责任放在了单元测试中,而不是在模式中。
我认为关于永不直接使用外键的引用旨在表明,使用ORM的“正常”行为是通过请求议程对象(print agendaItem.time_unit.name
)来访问time_unit,而不是明确地请求外键并检索相关对象(timeUnitID = agendaItem.timeUnitID; print time_unit.findByID(timeUnitID
)。我认为这不是针对外键的建议。
答案 1 :(得分:0)
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
,这在使用其他表时是不需要的。