【问题标题】:Composite Key of Composite Keys组合键的组合键
【发布时间】:2012-05-25 23:16:48
【问题描述】:

我正在尝试了解为我正在创建的数据模型采用什么方法。我有两个最初使用复合键创建的表。我现在添加第三个表,它是前两个表的连接表,这将产生一个由三个字段和两个外键组成的复合键,每个外键有 2 个字段。这很可能在 MySQL 中,并与某种 Java 持久性框架一起使用。我一直喜欢使用复合键,因为它似乎是一种更自然的方式来表示数据,但我想确保以后不会让自己陷入痛苦的世界。我应该继续使用提到的方法还是只是在表上创建一些自动递增的 id?

【问题讨论】:

    标签: java mysql sql hibernate jpa


    【解决方案1】:

    连接表的代理键在限制与外键的更深关系时有一个巨大的缺点。我们需要一个 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)
    );
    

    问题: 我们到底应该如何强制注册和班级都参考同一个学期?(简短回答:我们可以't)

    复合键也是如此——在这里我不需要额外的键来强制执行外键的唯一组合,主键默认为我执行此操作:

    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_namestudent_namesubject_name 值(而不必遍历多个级别的架构。)您可能会比这个最简单的例子有更多的层次,然后就更加明显地回到任何父关系是多么容易。

    改变架构并不更复杂,但即使是这样:数据建模难道不是关于建模数据吗?我们可以仅仅因为我们喜欢代理键就抛弃参照完整性吗?

    注意:Hibernate确实处理复合键。 DataMapper for Ruby 也是如此。

    【讨论】:

      【解决方案2】:

      Hibernate 建议使用纯技术、自动生成的非复合键(纯连接表除外)。出于很好的理由,IMO。

      使用复合键,您的映射会更加困难。由于更复杂的索引,性能会降低。通用编程会更难,因为您需要两到三个 long 来识别给定实体,而不是能够识别给定的实体(例如,考虑 URL 和表单,您必须在其中放置两个或三个参数/隐藏字段到您的网址/表单)。

      当然,如果键是有效的,那就更糟了,因为您有时必须更改主键的一部分,这将迫使您更新对该主键的所有引用。

      【讨论】:

      • 有反对意见:“性能会降低”。我同意所有其余的(更难编程,ORM 和框架无法处理复合 FK 等)
      【解决方案3】:

      如果您的多对多是纯关系(本身没有属性)并且永远不会被引用,则只需使用复合键。

      如果它可能有自己的属性或被引用(如实例化为一个类),您可能需要一个代理键,因为许多ORM 要求id 是一个整数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-21
        • 1970-01-01
        • 2020-02-02
        • 2018-01-03
        • 1970-01-01
        • 2014-02-18
        • 2011-04-14
        相关资源
        最近更新 更多