@OneToOne 表关联

AI摘要
JPA的@OneToOne注解用于定义实体间一对一关联关系,支持单向或双向引用。核心属性包括mappedBy(主控方指定)、cascade(级联操作)、fetch(加载策略)和optional(空值约束)。ORM框架自动维护数据库外键关系,简化关联数据操作。

在 Java Persistence API(JPA,如 Hibernate 等 ORM 框架)中,@OneToOne 是用于定义两个实体类之间一对一关联关系的注解。它表示一个实体(Entity)与另一个实体之间存在 “一一对应” 的映射关系,即一个实体的实例只能对应另一个实体的一个实例,反之亦然。

  • 单向引用:只有一个实体持有另一个实体的引用(字段),被引用的实体 “不知道” 对方的存在。
  • 双向引用:两个实体互相持有对方的引用(字段),彼此 “知道” 对方的存在(需通过 mappedBy 明确主控方)。

核心作用

@OneToOne 的核心是描述实体间的依赖关系,例如:

  • 一个 Person(人)对应一个 IdCard(身份证);
  • 一个 User(用户)对应一个 UserProfile(用户资料);
  • 一个 Order(订单)对应一个 Invoice(发票)。

通过 @OneToOne 注解,ORM 框架(如 Hibernate)会自动在数据库中维护这种关系(例如生成外键约束),并简化开发者对关联数据的操作(无需手动编写关联查询 SQL)。

关键属性与用法

@OneToOne 有几个常用属性,用于控制关联关系的细节:

1. mappedBy(最核心属性)

用于指定关联关系的主控方(由哪个实体维护外键),避免双向关联时的 “循环依赖” 或 “重复维护” 问题。

  • 场景:当两个实体双向引用(如 UserUserProfile 字段,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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!