1. 概述

在本教程中,我们将学习如何使用 JPA 提供的 @Embedded@Embeddable 注解,将一个包含嵌套属性的实体类映射到单一数据库表中。

这种技术非常适合将某些具有业务含义的字段组合成一个类,同时又不希望为它单独建表的场景。


2. 数据模型背景

我们先定义一个名为 company 的数据库表,用于存储公司信息,包括名称、地址、联系电话以及联系人信息。

对应的 Java 类如下:

public class Company {

    private Integer id;

    private String name;

    private String address;

    private String phone;

    private String contactFirstName;

    private String contactLastName;

    private String contactPhone;

    // standard getters, setters
}

很明显,联系人信息应该抽象成一个独立的类。

但问题在于:我们并不希望为这个联系人信息单独创建一张表。

那么,JPA 提供了什么机制来解决这个问题呢?答案就是 @Embeddable@Embedded


3. @Embeddable

@Embeddable 注解用于标记一个类可以被其他实体类嵌入使用。

我们可以将联系人信息封装到一个类中,并使用该注解:

@Embeddable
public class ContactPerson {

    private String firstName;

    private String lastName;

    private String phone;

    // standard getters, setters
}

✅ 这个类本身不会被映射成一张表,而是作为其他实体类的一部分存在。


4. @Embedded

@Embedded 注解用于在实体类中嵌入一个 @Embeddable 类型的字段。

接下来我们修改 Company 类,使用 ContactPerson 替代原先的联系人字段:

@Entity
public class Company {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    private String address;

    private String phone;

    @Embedded
    private ContactPerson contactPerson;

    // standard getters, setters
}

⚠️ 此时,JPA 会将 ContactPerson 的字段作为 Company 表的列进行映射。

但问题来了:这些字段会被如何命名?默认命名方式是否符合我们的预期?


5. 自定义嵌套字段列名:@EmbeddedColumnNaming

我们可以使用 @EmbeddedColumnNaming 来自定义嵌套字段的列名前缀。

默认情况下,Hibernate 会以嵌套字段名作为前缀。比如 contactPerson.firstName 会被映射为 contactPerson_firstName

如果我们想自定义前缀或去掉前缀,可以使用如下方式:

自定义前缀

@Embedded
@EmbeddedColumnNaming("contact_")
private ContactPerson contactPerson;

此时列名会变成 contact_firstName, contact_lastName, contact_phone

去除前缀

@Embedded
@EmbeddedColumnNaming("")
private ContactPerson contactPerson;

此时列名就是 firstName, lastName, phone


6. 属性覆盖:@AttributeOverrides@AttributeOverride

在原始的 Company 类中,联系人字段是 contactFirstName,而在 ContactPerson 类中变成了 firstName

JPA 默认会将 firstName 映射为 first_name,而原来的 phone 字段也会映射为 phone,这会导致字段冲突。

为了解决这个问题,我们可以使用 @AttributeOverrides@AttributeOverride显式指定列名

@Embedded
@AttributeOverrides({
  @AttributeOverride(name = "firstName", column = @Column(name = "contact_first_name")),
  @AttributeOverride(name = "lastName", column = @Column(name = "contact_last_name")),
  @AttributeOverride(name = "phone", column = @Column(name = "contact_phone"))
})
private ContactPerson contactPerson;

✅ 这样我们就可以精确控制每个字段映射到数据库的列名,避免命名冲突。

⚠️ 注意:这些注解是加在字段上的,因此在不同的实体中可以有不同的映射规则。


7. 总结

本文演示了如何通过 JPA 的 @Embedded@Embeddable 注解,将一个嵌套对象映射到同一个数据库表中。

我们还介绍了以下关键点:

  • @Embeddable:定义可被嵌入的类
  • @Embedded:在实体类中嵌入 @Embeddable 类型
  • @EmbeddedColumnNaming:控制嵌套字段的列名前缀
  • @AttributeOverrides / @AttributeOverride:覆盖默认的列名映射,解决字段冲突

通过合理使用这些注解,可以在保持代码结构清晰的同时,避免不必要的数据库表拆分,非常适合用于组合型字段的封装。


原始标题:Jpa @Embedded and @Embeddable | Baeldung

» 下一篇: Java每周,问题277