【Java-MySQL】主键、外键有什么区别?

主键(Primary Key)和外键(Foreign Key)的区别 是 Java + MySQL 开发中最基础也是最重要的数据库概念之一。下面用清晰的表格 + 详细解释,帮助你彻底搞懂:

1. 一目了然的对比表

对比维度主键 (Primary Key)外键 (Foreign Key)
作用唯一标识本表的一条记录建立两张表之间的关联关系
是否允许为空不允许(NOT NULL)允许(除非你加 NOT NULL)
是否唯一必须唯一可以重复(一个部门可以有多个员工)
一张表能有几个最多只能有1个(复合主键也算一个)一张表可以有多个外键
是否自动创建索引是(主键索引,聚簇索引)是(普通索引,非聚簇)
能否被其他表引用可以(被其他表的外键引用)不能再被其他表的外键引用(除非它同时也是主键)
对数据的影响插入时必须保证唯一且不为空插入/修改时会检查引用完整性(参照完整性约束)
删除/更新时的行为一般不会删除主键记录(或级联删除)有4种约束动作:RESTRICT / CASCADE / SET NULL / SET DEFAULT

2. 详细解释

主键 (Primary Key)

  • 核心作用唯一标识表中的每一条记录(就像人的身份证号)。
  • 特点:
  • 不能重复、不能为空。
  • MySQL 中主键会自动创建聚簇索引(数据物理存储顺序按主键排序),查询速度最快。
  • 推荐使用:INT/BIGINT AUTO_INCREMENTUUID(视业务而定)。
  • 示例:
CREATE TABLE users (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,   -- 主键
    username    VARCHAR(50) UNIQUE NOT NULL,
    ...
);

外键 (Foreign Key)

  • 核心作用维护两个表之间的参照完整性(Referential Integrity)。
  • 特点:
  • 外键的值必须来自被引用表的主键(或唯一键)。
  • 防止“孤儿记录”(比如删除了一个部门,结果还有员工属于这个部门)。
  • 示例(经典:部门-员工):
CREATE TABLE departments (
    dept_id     INT PRIMARY KEY AUTO_INCREMENT,
    dept_name   VARCHAR(50) NOT NULL
);

CREATE TABLE employees (
    emp_id      BIGINT PRIMARY KEY AUTO_INCREMENT,
    emp_name    VARCHAR(50) NOT NULL,
    dept_id     INT,                                      -- 外键

    CONSTRAINT fk_emp_dept 
        FOREIGN KEY (dept_id) 
        REFERENCES departments(dept_id)
        ON DELETE RESTRICT        -- 或 CASCADE / SET NULL
        ON UPDATE CASCADE
);

3. 外键的 4 种约束动作(非常重要!)

当你对父表(departments)进行 DELETE 或 UPDATE 时,MySQL 如何处理子表(employees):

动作含义推荐场景
RESTRICT / NO ACTION禁止删除/修改(默认)数据安全要求高
CASCADE级联删除/更新(父删子也删)日志、临时数据
SET NULL把子表的外键字段设为 NULL允许员工不属于任何部门
SET DEFAULT把子表的外键设为默认值(MySQL 不常用)

4. Java 开发中实际使用建议

  1. 强烈推荐使用外键(除非性能极致要求):
  • 防止脏数据
  • 代码更清晰(JPA/Hibernate 会自动映射关联关系)
  1. 高并发、大数据量场景 常见做法:
  • 开发环境、测试环境:保留外键(便于发现问题)
  • 生产环境:去掉外键约束,只在代码层面(Service 层)保证完整性 + 建立普通索引
  • 理由:外键会导致级联锁表,影响并发性能
  1. JPA/Hibernate 写法示例
@Entity
@Table(name = "employees")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id", foreignKey = @ForeignKey(name = "fk_emp_dept"))
    private Department department;
}

总结(一句话记住):

  • 主键 = “我是谁”(本表唯一标识)
  • 外键 = “我爸是谁”(指向另一个表的标识)

主键解决本表记录的唯一性,外键解决表与表之间的关联完整性


需要我再给你补充:

  • 复合主键 vs 单列主键?
  • 物理外键 vs 逻辑外键(代码控制)性能对比?
  • 实际项目中如何优雅处理外键?

随时问我!

文章已创建 5130

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部