JPA 中的一对一关系

1. 概述

在本教程中,我们将了解在 JPA 中创建一对一映射的不同方法。

2. 说明

假设我们正在构建一个用户管理系统,我们的老板要求我们为每个用户存储一个邮寄地址。一个用户将有一个邮寄地址,一个邮寄地址将只有一个与之绑定的用户。

这是用户和 地址实体之间的一对一关系的示例。

让我们在接下来的部分中看看如何实现这一点。

3. 使用外键

3.1. 使用外键建模

让我们看一下下面的 ER 关系图,它表示基于外键的一对一映射:

 在此示例中,用户中的address_id列是要寻址外键

3.2. 在 JPA 中使用外键实现

首先,让我们创建 User 类并对其进行适当的注释:

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //... 

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    // ... getters and setters
}

请注意,我们将@OneToOne注释放在相关实体字段“地址”上

此外,我们需要放置@JoinColumn注释来配置用户表中映射到地址表中主键的列的名称。如果我们不提供名称,Hibernate 将遵循一些规则来选择默认名称。

最后,在下一个实体中请注意,我们不会在那里使用@JoinColumn注释。这是因为我们只需要在外键关系的拥有方。简单地说,无论谁拥有外键列,都会获得@JoinColumn注释。

“地址”实体变得简单一些:

@Entity
@Table(name = "address")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(mappedBy = "address")
    private User user;

    //... getters and setters
}

我们还需要将@OneToOne注释放在这里。这是因为这是一种双向关系关系的地址端称为非拥有端。

4. 使用共享主键

4.1. 使用共享主键建模

在此策略中,我们将地址表的主键列 (user_id) 标记为用户表的外键,而不是创建新的列address_id:

 我们通过利用这些实体之间具有一对一关系的事实来优化存储空间。

4.2. 在 JPA 中使用共享主键实现

请注意,我们的定义仅略有变化:

@Entity
@Table(name = "users")
public class User {

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

    //...

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Address address;

    //... getters and setters
}
@Entity
@Table(name = "address")
public class Address {

    @Id
    @Column(name = "user_id")
    private Long id;

    //...

    @OneToOne
    @MapsId
    @JoinColumn(name = "user_id")
    private User user;
   
    //... getters and setters
}

映射的 By 属性现在已移动到 User 类,因为地址表中现在存在外键。我们还添加了@PrimaryKeyJoinColumn批注,该批注指示 User 实体的主键用作关联地址实体的外键值。

我们仍然必须在 Address 类中定义一个@Id字段。但请注意,这引用了user_id列,并且不再使用@GeneratedValue批注。此外,在引用用户的字段上,我们添加了@MapsId注释,指示将从 User 实体复制主键值

5. 使用联接表

一对一映射可以有两种类型:可选映射和强制映射。 到目前为止,我们只看到强制性关系。

现在,让我们想象一下,我们的员工与工作站相关联。这是一对一的,但有时员工可能没有工作站,反之亦然。

5.1. 使用联接表建模

到目前为止,我们讨论的策略迫使我们将 null 值放在列中以处理可选关系。

通常,当我们考虑连接表时,我们会想到多对多关系但是在这种情况下使用连接表可以帮助我们消除这些空值

 

现在,每当我们有关系时,我们将在emp_workstation表中创建一个条目,并完全避免空值

5.2. 在 JPA 中使用联接表实现

我们的第一个例子@JoinColumn使用。这一次,我们将使用@JoinTable

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "emp_workstation", 
      joinColumns = 
        { @JoinColumn(name = "employee_id", referencedColumnName = "id") },
      inverseJoinColumns = 
        { @JoinColumn(name = "workstation_id", referencedColumnName = "id") })
    private WorkStation workStation;

    //... getters and setters
}
@Entity
@Table(name = "workstation")
public class WorkStation {

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

    //...

    @OneToOne(mappedBy = "workStation")
    private Employee employee;

    //... getters and setters
}

@连接表指示休眠在维护关系时使用连接表策略。

此外,Employee 是此关系的所有者,因为我们选择在其上使用联接表批注。

6. 结论

在本文中,我们学习了在 JPA 和休眠中维护一对一关联的不同方法,以及何时使用每种方法。

本文的源代码可以在 GitHub 上找到。

通过参考学习春季数据JPA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值