1. 概述

在 JPA 中,实体之间的关联关系可以是单向的,也可以是双向的。所谓单向,就是只在一端定义关联属性;而双向则是在两端都定义。

✅ 关联的方向并不会影响数据库层面的映射结构,它只决定了我们在 Java 代码中如何使用这个关系。

对于双向关联,我们通常会区分:

  • 拥有方(Owning Side)
  • 被拥有方(Inverse / Referencing Side)

📌 @JoinColumn 注解用于指定外键列名,用来映射实体间的关联关系或集合元素。
📌 而 mappedBy 属性则是用来标记非拥有方,即引用拥有方的那一侧。

本篇文章将带你搞清楚 @JoinColumnmappedBy 的本质区别,并演示它们在一对多(One-to-Many)场景下的正确用法。

2. 初始设置

为了方便讲解,我们假设有两个实体类:EmployeeEmail

显而易见的是:

  • 一个员工可以有多个邮箱 ✅
  • 一个邮箱只能属于一个员工 ❌

因此,它们之间是一对多的关系:

Employee - Email

在数据库模型中,我们会在 Email 表中添加一个外键字段 employee_id,指向 Employee 表的主键 id

3. @JoinColumn 注解

在一对多/多对一关系中,拥有方通常定义在“多”的那一侧,也就是包含外键的那一边。

⚠️ 使用 @JoinColumn 可以显式地指定外键列名,告诉 JPA 应该使用哪个字段来维护这个关联关系。

来看一下 Email 实体的写法:

@Entity
public class Email {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id")
    private Employee employee;

    // ...
}

这段代码的意思是:Email 表中将有一个名为 employee_id 的外键列,指向 Employee 实体的主键 id

4. mappedBy 属性

当我们已经定义了拥有方后,Hibernate 就已经具备了建立数据库关联所需的所有信息。

如果想把这种关系变成双向的,只需要在另一端加上 mappedBy 即可。

📌 mappedBy 的值就是拥有方实体中用来表示该关系的那个属性名

下面是 Employee 实体的定义:

@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "employee")
    private List<Email> emails;
    
    // ...
}

通过上面的配置,我们就成功建立了 EmployeeEmail 之间的双向关联。

5. 总结

这篇文章我们对比了 @JoinColumnmappedBy 的核心区别,并展示了它们在一对多双向关联中的典型用法。

@JoinColumn 是用在外键拥有方的,用来指定实际的物理列名;
mappedBy 则用在非拥有方,告诉 JPA 哪个属性是对方用来指向自己的。

最后,完整的示例代码可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate-annotations


原始标题:Difference Between @JoinColumn and mappedBy