@OneToOne 表关联
在 Java Persistence API(JPA,如 Hibernate 等 ORM 框架)中,@OneToOne
是用于定义两个实体类之间一对一关联关系的注解。它表示一个实体(Entity)与另一个实体之间存在 “一一对应” 的映射关系,即一个实体的实例只能对应另一个实体的一个实例,反之亦然。
- 单向引用:只有一个实体持有另一个实体的引用(字段),被引用的实体 “不知道” 对方的存在。
- 双向引用:两个实体互相持有对方的引用(字段),彼此 “知道” 对方的存在(需通过
mappedBy
明确主控方)。
核心作用
@OneToOne
的核心是描述实体间的依赖关系,例如:
- 一个
Person
(人)对应一个IdCard
(身份证); - 一个
User
(用户)对应一个UserProfile
(用户资料); - 一个
Order
(订单)对应一个Invoice
(发票)。
通过 @OneToOne
注解,ORM 框架(如 Hibernate)会自动在数据库中维护这种关系(例如生成外键约束),并简化开发者对关联数据的操作(无需手动编写关联查询 SQL)。
关键属性与用法
@OneToOne
有几个常用属性,用于控制关联关系的细节:
1. mappedBy
(最核心属性)
用于指定关联关系的主控方(由哪个实体维护外键),避免双向关联时的 “循环依赖” 或 “重复维护” 问题。
场景:当两个实体双向引用(如
User
有UserProfile
字段,UserProfile
也有User
字段)时,需用mappedBy
声明 “由对方维护外键”。// 主控方:User(维护外键) @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 关联 UserProfile,外键在 User 表中 @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "profile_id") // 数据库中外键列名 private UserProfile profile; } // 被控方:UserProfile(由 User 维护关系) @Entity public class UserProfile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // mappedBy = "profile" 表示:关系由 User 类的 profile 字段维护 @OneToOne(mappedBy = "profile") private User user; }
2.
cascade
(级联操作)
定义对当前实体操作时,是否对关联实体执行相同操作(如保存、删除等)。常见值:
CascadeType.PERSIST
:保存当前实体时,自动保存关联实体;CascadeType.REMOVE
:删除当前实体时,自动删除关联实体;CascadeType.ALL
:包含所有级联操作。
3. fetch
(加载策略)
指定关联实体的加载方式:
FetchType.EAGER
(默认):加载当前实体时,立即加载关联实体(“饿汉式”);FetchType.LAZY
:加载当前实体时,延迟加载关联实体(仅当调用关联实体的 getter 方法时才加载,“懒汉式”)。
4. optional
指定关联实体是否可为 null
,默认为 true
(允许为 null)。若设为 false
,则表示关联实体必须存在(数据库层面会生成非空约束)。
数据库层面的体现
@OneToOne
会在数据库中通过外键约束实现一对一关系:
- 外键通常存在于 “主控方” 对应的表中(由
@JoinColumn
指定列名); - 外键值唯一(确保一个实体只能对应另一个实体的一个实例)。
/**
单向引用的实现(以 @OneToOne 为例)
假设场景:User(用户)必须关联一个 IdCard(身份证),但业务上只需要 “通过用户查身份证”,不需要 “通过身份证查用户”。此时只需定义单向引用即可。
*/
// 主控方:User(持有 IdCard 的引用,维护外键)
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
// 单向关联 IdCard:仅 User 持有 IdCard 的引用
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_card_id") // 外键列在 user 表中,名为 id_card_id
private IdCard idCard;
// getter/setter
}
// 被关联方:IdCard(不持有 User 的引用,完全“不知道”User 的存在)
@Entity
public class IdCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String cardNo; // 身份证号
private String address;
// 注意:这里没有 User 类型的字段,即不反向引用 User
// getter/setter
}
本作品采用《CC 协议》,转载必须注明作者和本文链接