介绍
在基于 Java 的应用程序开发领域,Spring 框架和 JPA(Java Persistence API)这两个工具彻底改变了开发人员处理持久数据的方式。@Entity通过诸如、@Table等注释,JPA 提供了对传统关系数据库操作的抽象。在这些注释中,@Embedded和@Embeddable对于在实体中嵌入对象特别有用。本指南探讨了这些注释的本质和用例。
了解 @Embedded 和 @Embeddable 的需求
由于现代应用程序呈现出巨大的复杂性,有效管理持久数据至关重要。@Embedded为了充分掌握注释的必要性@Embeddable,我们需要研究开发人员在关系数据库中建模和映射数据时经常遇到的挑战。
关系数据库和面向对象悖论
面向对象的编程允许开发人员轻松创建复杂的数据模型。您可以在对象中包含对象、继承、多态性等等。然而,这些面向对象的模型并不总是能顺利地转化为扁平的、基于表的关系数据库结构。
例如,考虑一下不起眼的User物体。典型的用户可能有简单的字段,例如姓名或电子邮件,但是地址呢?地址本身很复杂,由街道、城市、州、国家等组成。
在面向对象的世界中,您可能会创建一个Address对象并将其嵌入到该User对象中。但是,当将其转换为关系数据库时,您面临一个决定:
这两种方法都有其缺点。如果用户有多个地址(例如,账单、运输等),则扁平化字段可能会导致冗余的列名称。同时,为每个复杂对象创建单独的表可能会导致数据库架构臃肿和复杂的连接操作,从而影响性能。
输入@Embeddable:简化对象表示
JPA 引入了@Embeddable注释来弥补这一差距。通过将一个类标记为@Embeddable,您就表明该类虽然很复杂,但不需要在数据库中拥有其表。相反,它的字段可以直接嵌入到另一个实体中。
例如,将Address类标记为@Embeddable意味着它的字段(街道、城市、国家等)可以直接集成到另一个实体的表中,例如User. 这样,我们就可以保持面向对象的设计,而不会影响数据库操作的效率。
@Embedded:无缝集成
虽然@Embeddable将类标记为可嵌入,但@Embedded注释在另一个实体中使用来嵌入它。正是这两个注释的双重作用为我们的问题提供了一个干净的解决方案。
通过在实体内的字段@Embedded上使用注释,您实际上是在告诉 JPA:“嘿,从类中获取字段并将它们直接嵌入到表中。” 这消除了对冗余表和繁琐的连接操作的需要,同时仍然保留了开发人员所珍视的面向对象的设计。AddressUserAddressUser
现实世界的类比
想想@Embeddable并@Embedded喜欢收拾行李去旅行。有些物品,如袜子或内衣,不需要放在袋子里;它们可以嵌入您的手提箱中。注释@Embeddable就像将这些项目标记为“适合嵌入”。收拾行李时,您可以决定将它们放入哪个手提箱或袋子中。这就是@Embedded正在运行的注释,指定这些项目(或字段)所在的位置。
深入研究@Embeddable
在庞大的 JPA 注释生态系统中,@Embeddable占据着独特的位置,在对象关系映射和数据库设计之间提供了微妙的平衡。为了了解它的威力和局限性,让我们剖析它的基本原理、优点以及何时使用它。
@Embeddable 的基础知识
其核心是@Embeddable向 JPA 发出信号,表明它所注释的类不是一个成熟的实体。它没有生命周期,也不直接对应于数据库中的唯一表。相反,它是在实际实体中重用的组件。
在面向对象编程的世界中,将复杂的对象分解为更小、更易于管理的部分是很常见的。然而,这些较小的部分并不总是整齐地映射到关系数据库中。注释@Embeddable为这一差距提供了一座桥梁。通过用 指定一个类@Embeddable,您就表明它已准备好嵌入其他实体中,而无需单独的表。
使用@Embeddable的好处
潜在的陷阱和注意事项
虽然@Embeddable提供了许多好处,但明智地使用它至关重要:
一个说明性的例子
想象一个DateRange包含开始日期和结束日期的类。这个简单的类可用于多种场景:定义学生的注册期限、指示优惠券的有效性或指定活动的持续时间。DateRange您可以将类定义为,@Embeddable然后将其嵌入到需要的位置,而不是在多个实体之间复制开始日期和结束日期字段。
@Embeddable
public class DateRange {
private LocalDate startDate;
private LocalDate endDate;
// Constructors, getters, setters, etc.
}
通过使用这个可嵌入类,您可以提高整个应用程序的可重用性、一致性和更简洁的设计。
使用@Embedded
将类标记为 后@Embeddable,您就可以将其嵌入到您的主要实体中。注释@Embedded就是为了这个目的。但是,从将某些东西标记为可嵌入到实际嵌入它的过程有其自身的细微差别。在本节中,我们将探讨@Embedded注释的剖析、它与 的相互作用@Embeddable以及需要注意的事项。
@Embedded 剖析
从本质上讲,@Embedded注释很简单。它在实体中使用,指示特定字段是嵌入对象,派生自标有 的类@Embeddable。
通过使用@Embedded,您实质上为 JPA 提取嵌入对象的字段并将它们表示为托管实体表中的列开了绿灯。
@Embedded 和 @Embeddable 的相互作用
值得注意的是,虽然@Embeddable和@Embedded看起来是配对的,但后者在技术上是可选的。如果您@Embeddable在没有注释的实体中使用类型@Embedded,JPA 仍然会识别它并表现得就像@Embedded存在一样。然而,明确的@Embedded注释可以增强可读性和理解性。
让我们回顾一下之前的例子:
@Entity
public class User {
@Id
private Long id;
private String name;
private String email;
@Embedded
private Address address;
// Constructors, getters, setters, etc.
}
Address如前所述,该类被标记为@Embeddable。在User实体内,通过使用@Embeddedforaddress字段,我们指示 JPA 将Address类的字段直接映射为表中的列User。
需要注意的事项
在实践中
使用@Embedded通常是出于设计清晰度而做出的决定。假设您有一个Event实体,并且您想要定义事件发生的时间段。如果您将DateRange类定义为@Embeddable,则可以将其无缝嵌入到Event实体中:
@Entity
public class Event {
@Id
private Long eventId;
private String eventName;
@Embedded
private DateRange eventPeriod;
// ... additional fields, constructors, getters, setters ...
}
这样的设计决策简化了数据库操作,减少了冗余,并保持了面向对象的设计理念。
高级用例:属性覆盖和自定义
JPA 的主要优势之一是其灵活性。@Embedded当与和 一起使用时,这种适应性就会大放异彩@Embeddable。虽然基本用例有助于减少冗余并促进干净的代码,但有时现实场景需要进一步定制。在这种情况下,JPA 提供了@AttributeOverride和等工具@AttributeOverrides。
使用@AttributeOverride自定义列名
在实体包含多个相同@Embeddable类型字段的情况下,可能会出现列名称冲突。为了防止这种情况,JPA 提供了@AttributeOverride注释,允许您自定义嵌入属性的列名称。
User考虑我们之前的具有帐单地址和送货地址的实体示例:
@Entity
public class User {
@Id
private Long id;
private String name;
@Embedded
@AttributeOverride(name="street", column=@Column(name="billing_street"))
private Address billingAddress;
@Embedded
@AttributeOverride(name="street", column=@Column(name="shipping_street"))
private Address shippingAddress;
// ... additional fields, constructors, getters, setters ...}
在此示例中,为了防止可嵌入类street中的字段Address发生冲突,我们使用@AttributeOverride自定义表中的列名称User。
使用 @AttributeOverrides 进行多次覆盖
当需要重写可嵌入类的多个属性时,可以使用注解@AttributeOverrides,它本质上是将多个@AttributeOverride注解分组。
扩展前面的示例:
@Entity
public class User {
@Id
private Long id;
private String name;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="street", column=@Column(name="billing_street")),
@AttributeOverride(name="city", column=@Column(name="billing_city"))})
private Address billingAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="street", column=@Column(name="shipping_street")),
@AttributeOverride(name="city", column=@Column(name="shipping_city"))})
private Address shippingAddress;
// ... additional fields, constructors, getters, setters ...}
此设置可确保帐单地址和送货地址的street和city字段在表中具有不同的列名称User。
高级定制的注意事项
结论
Spring的@Embedded和@Embeddable注释提供了一种有效的方法来管理实体中的复杂对象。它们简化了数据库架构和操作,使您可以避免不必要的表创建和连接。虽然适用于许多场景,但必须了解何时使用这些注释以及何时单独的实体(和表)可能更合适。与所有工具一样,它们的有效性取决于您的应用程序的特殊性及其要求。