我试图了解我正在创建的数据模型采用什么方法。我有两个最初使用复合键创建的表。我现在添加第三个表,它是前两个表的连接表,这将产生一个由三个字段和两个外键组成的复合键,每个键有 2 个字段。这很可能在 MySQL 中,并与某种 Java 持久性框架一起使用。我一直喜欢使用复合键,因为它似乎是一种更自然的方式来表示数据,但我想确保我以后不会让自己陷入痛苦的世界。我应该继续使用提到的方法还是只是在表上创建一些自动递增的 id?
3 回答
Hibernate 建议使用纯技术的、自动生成的、非复合键(纯连接表除外)。出于很好的理由,IMO。
使用复合键,您的映射将更加困难。由于更复杂的索引,性能会降低。一般编程会更难,因为您需要两到三个长整数,而不是能够识别给定的实体(例如,考虑 URL 和表单,您必须在其中放置两个或三个参数/隐藏字段到您的 URL/表单)。
当然,如果键是功能性的,那就更糟了,因为您将不得不在某个时候更改主键的一部分,这将迫使您更新对该主键的所有引用。
在约束与外键更深的关系时,连接表的代理键有一个巨大的缺点。我们需要一个 6 表设置来演示它。
基表:
CREATE TABLE semester (semester_id INTEGER PRIMARY KEY, semester_name VARCHAR(40));
CREATE TABLE student (student_id INTEGER PRIMARY KEY, student_name VARCHAR(40));
CREATE TABLE subject (subject_id INTEGER PRIMARY KEY, subject_name VARCHAR(40));
然后让我们连接它们:
CREATE TABLE enrollment (
enrollment_id INTEGER PRIMARY KEY,
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
room_number INTEGER,
FOREIGN KEY (semester_id) REFERENCES semester (semester_id),
FOREIGN KEY (student_id) REFERENCES student (student_id),
UNIQUE INDEX (semester_id, student_id)
);
-- similarly ...
CREATE TABLE class(class_id ..., semester_id ..., subject_id ..., class_number ...);
到目前为止,一切都很棒。但是,我们需要更多地连接它们:
CREATE TABLE grades (
student_in_class_id INTEGER PRIMARY KEY,
enrollment_id INTEGER NOT NULL,
class_id INTEGER NOT NULL,
grade char(1),
FOREIGN KEY enrollment (enrollment_id),
FOREIGN KEY class (class_id),
UNIQUE INDEX (enrollment_id, class_id)
);
问题: 我们到底应该如何强制招生和班级都参考同一个学期?(简短回答:我们不能)
复合键也是如此——在这里我不需要额外的键来强制执行外键的唯一组合,主键默认为我执行此操作:
CREATE TABLE enrollment (
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
room_number INTEGER,
PRIMARY KEY (semester_id, student_id),
FOREIGN KEY (semester_id) REFERENCES semester (semester_id),
FOREIGN KEY (student_id) REFERENCES student (student_id)
);
-- along the same lines...
class(semester_id ..., subject_id ..., class_number ...)
然后是不祥等级表:
CREATE TABLE grades (
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
subject_id INTEGER NOT NULL,
PRIMARY KEY (semester_id, student_id, subject_id),
FOREIGN KEY (semester_id, student_id) REFERENCES enrollment(semester_id, student_id),
FOREIGN KEY (semester_id, subject_id) REFERENCES class(semester_id, subject_id)
);
这样,我的人际关系就得到了适当的表达和约束。
额外的好处:我可以通过简单的连接获得我的semester_name
,student_name
和subject_name
值(而不是必须通过多个级别遍历模式。)您可能会拥有比这个最简单的示例更多的级别,然后它会更加明显它是返回到任何父关系。
更改模式并不复杂,但即使是这样:数据建模不是关于建模数据吗?我们可以仅仅因为我们喜欢代理键就抛弃参照完整性吗?
注意: Hibernate确实处理复合键。DataMapper for Ruby 也是如此。
如果您的多对多是纯关系(本身没有属性)并且永远不会被自身引用,则只需使用复合键。
如果它可能有自己的属性或被引用(如实例化为类),您可能需要一个代理键,因为许多ORM
' 要求id
是单个整数。