在基于 Java 的应用程序开发领域,Spring 框架和 JPA(Java Persistence API)这两个工具彻底改变了开发人员处理持久数据的方式。@Entity通过诸如、@Table等注释,JPA 提供了对传统关系数据库操作的抽象。在这些注释中,@Embedded和@Embeddable对于在实体中嵌入对象特别有用。本指南探讨了这些注释的本质和用例。
由于现代应用程序呈现出巨大的复杂性,有效管理持久数据至关重要。@Embedded为了充分掌握注释的必要性@Embeddable,我们需要研究开发人员在关系数据库中建模和映射数据时经常遇到的挑战。
面向对象的编程允许开发人员轻松创建复杂的数据模型。您可以在对象中包含对象、继承、多态性等等。然而,这些面向对象的模型并不总是能顺利地转化为扁平的、基于表的关系数据库结构。
例如,考虑一下不起眼的User物体。典型的用户可能有简单的字段,例如姓名或电子邮件,但是地址呢?地址本身很复杂,由街道、城市、州、国家等组成。
这两种方法都有其缺点。如果用户有多个地址(例如,账单、运输等),则扁平化字段可能会导致冗余的列名称。同时,为每个复杂对象创建单独的表可能会导致数据库架构臃肿和复杂的连接操作,从而影响性能。
JPA 引入了@Embeddable注释来弥补这一差距。通过将一个类标记为@Embeddable,您就表明该类虽然很复杂,但不需要在数据库中拥有其表。相反,它的字段可以直接嵌入到另一个实体中。
例如,将Address类标记为@Embeddable意味着它的字段(街道、城市、国家等)可以直接集成到另一个实体的表中,例如User. 这样,我们就可以保持面向对象的设计,而不会影响数据库操作的效率。
虽然@Embeddable将类标记为可嵌入,但@Embedded注释在另一个实体中使用来嵌入它。正是这两个注释的双重作用为我们的问题提供了一个干净的解决方案。
通过在实体内的字段@Embedded上使用注释,您实际上是在告诉 JPA:“嘿,从类中获取字段并将它们直接嵌入到表中。” 这消除了对冗余表和繁琐的连接操作的需要,同时仍然保留了开发人员所珍视的面向对象的设计。AddressUserAddressUser
想想@Embeddable并@Embedded喜欢收拾行李去旅行。有些物品,如袜子或内衣,不需要放在袋子里;它们可以嵌入您的手提箱中。注释@Embeddable就像将这些项目标记为“适合嵌入”。收拾行李时,您可以决定将它们放入哪个手提箱或袋子中。这就是@Embedded正在运行的注释,指定这些项目(或字段)所在的位置。
在庞大的 JPA 注释生态系统中,@Embeddable占据着独特的位置,在对象关系映射和数据库设计之间提供了微妙的平衡。为了了解它的威力和局限性,让我们剖析它的基本原理、优点以及何时使用它。
在面向对象编程的世界中,将复杂的对象分解为更小、更易于管理的部分是很常见的。然而,这些较小的部分并不总是整齐地映射到关系数据库中。注释@Embeddable为这一差距提供了一座桥梁。通过用 指定一个类@Embeddable,您就表明它已准备好嵌入其他实体中,而无需单独的表。
@Embeddable public class DateRange { private LocalDate startDate; private LocalDate endDate; // 堆代码 duidaima.com // Constructors, getters, setters, etc. }通过使用这个可嵌入类,您可以提高整个应用程序的可重用性、一致性和更简洁的设计。
将类标记为 后@Embeddable,您就可以将其嵌入到您的主要实体中。注释@Embedded就是为了这个目的。但是,从将某些东西标记为可嵌入到实际嵌入它的过程有其自身的细微差别。在本节中,我们将探讨@Embedded注释的剖析、它与 的相互作用@Embeddable以及需要注意的事项。
通过使用@Embedded,您实质上为 JPA 提取嵌入对象的字段并将它们表示为托管实体表中的列开了绿灯。
@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。
相同类型的多个可嵌入项: 如果一个实体具有多个相同@Embeddable类型的字段(例如 billingAddress 和shippingAddress),则确保列名称不冲突至关重要。使用@AttributeOverride或@AttributeOverrides自定义列名称。
可嵌入对象中的可嵌入对象: 虽然可以将可嵌入对象嵌套在其他可嵌入对象中,但这可能会导致复杂的层次结构,从而难以维护。确保设计保持逻辑性和直观性。
@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。
在实体包含多个相同@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。
@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。