在 JPA 和Hibernate中映射一对一关系的 4 种方法

本文介绍了在JPA和Hibernate中使用@OneToOne注释映射一对一关系的几种方法,包括带@JoinColumn的外键单向和双向映射、带@MapsId和@PrimaryKeyJoinColumn的共享主键单向和双向映射,还说明了每种方法的优缺点及动手教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有几种方法可以通过使用@OneToOne注释来映射JPA和Hibernate中的一对一关系,包括

  • 带@JoinColumn的外键单向和双向映射

  • 共享主键单向和双向映射,带@MapsId和@PrimaryKeyJoinColumn

本指南将向您展示如何进行映射以及每种方法的优缺点

考虑一个人和身份证之间的关系。一个人只有一张身份证,一张身份证只属于一个人

具有@JoinColumn和@OneToOne的外键双向映射

@OneToOne将放置在关系的两个实体上,则 mapBy 属性值指向关系所有者,该所有者在基础表中具有外键列

@Entity
public class Person {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @JoinColumn(name = "id_card_id")
    private IDCard idCard;

    ...
}
@Entity
public class IDCard {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(unique = true, nullable = false)
    private String code = UUID.randomUUID().toString();

    @OneToOne(mappedBy = "idCard")
    private Person person;

    ...
}

@JoinColumn指定外键列。它具有关联字段名称及其主键列名称的下划线字符串连接的默认名称

需要@OneToOne上的mappedBy属性才能指定双向映射。如果mappedBy不存在,JPA 和休眠将创建一个指向关系所有者的冗余外键列

mysql> describe idcard;  
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| code      | varchar(255) | NO   | UNI | NULL    |                |
| person_id | int(11)      | NO   | UNI | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

优点和缺点

  • 外键映射比共享主键映射更原生且易于理解

  • 父实体可以快速访问 CRUD 操作并将其级联到子关联

动手教程

具有@JoinColumn和@OneToOne的外键单向映射

@OneToOne将仅放置在一对一关系的 1 个实体上。我们不需要指定mappedBy 属性

@Entity
public class Person {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @JoinColumn(name = "id_card_id")
    private IDCard idCard;

    ...
}
@Entity
public class IDCard {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(unique = true, nullable = false)
    private String code = UUID.randomUUID().toString();

    ...
}

@JoinColumn指定外键列。它具有关联字段名称及其主键列名称的下划线字符串连接的默认名称

优点和缺点

  • 外键映射比共享主键映射更原生且易于理解

  • 外键单向映射比双向映射更不详细

具有@MapsId和@OneToOne的共享主键双向映射

关系的两个实体将使用相同的主键。在子实体中,共享主键也是外键

@Entity
public class Person {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
    private IDCard idCard;

    ...
}

@Entity
public class IDCard implements Serializable {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @JoinColumn(name = "person_id")
    @MapsId
    private Person person;

    ...
}

@OneToOne两个实体上都有标记。在子实体上,@MapsId标记在与 @OneToOne

@JoinColumn指定外键列。它具有关联字段名称及其主键列名称的下划线字符串连接的默认名称

优点和缺点

  • 父实体可以快速访问 CRUD 操作并将其级联到子关联

  • 外键映射将比共享主键映射更原生且易于理解

动手教程

具有@PrimaryKeyJoinColumn和@OneToOne的共享主键单向映射

关系的两个实体将使用相同的主键。在子实体中,共享主键也是外键

@Getter @Setter
@Entity
public class Person {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    ...
}

@Entity
public class IDCard {  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int personId;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @PrimaryKeyJoinColumn(name = "person_id", referencedColumnName = "id")
    private Person person;

    @Column(unique = true, nullable = false)
    private String code = UUID.randomUUID().toString();

    ...
}
复制

@OneToOne仅在子实体上与@PrimaryKeyJoinColumn一起标记

优点和缺点

  • 父实体无法访问或级联到子关联的 CRUD 操作

动手教程

在本文中,我们快速浏览了在JPA和Hibernate中映射一对一关系的各种方法,以及有关其优缺点的说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值