1. 概述
在 JPA 中,实体之间的关联关系可以是单向的,也可以是双向的。所谓单向,就是只在一端定义关联属性;而双向则是在两端都定义。
✅ 关联的方向并不会影响数据库层面的映射结构,它只决定了我们在 Java 代码中如何使用这个关系。
对于双向关联,我们通常会区分:
- 拥有方(Owning Side)
- 被拥有方(Inverse / Referencing Side)
📌 @JoinColumn
注解用于指定外键列名,用来映射实体间的关联关系或集合元素。
📌 而 mappedBy
属性则是用来标记非拥有方,即引用拥有方的那一侧。
本篇文章将带你搞清楚 @JoinColumn
和 mappedBy
的本质区别,并演示它们在一对多(One-to-Many)场景下的正确用法。
2. 初始设置
为了方便讲解,我们假设有两个实体类: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;
// ...
}
通过上面的配置,我们就成功建立了 Employee
和 Email
之间的双向关联。
5. 总结
这篇文章我们对比了 @JoinColumn
与 mappedBy
的核心区别,并展示了它们在一对多双向关联中的典型用法。
✅ @JoinColumn
是用在外键拥有方的,用来指定实际的物理列名;
✅ mappedBy
则用在非拥有方,告诉 JPA 哪个属性是对方用来指向自己的。
最后,完整的示例代码可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate-annotations