
在jpa/hibernate中,开发者有时会尝试在嵌入式类(@embeddable)中定义实体间的关联关系,尤其是在希望通过mappedby属性来指定@onetomany关系的非拥有方时。以下是一个典型的场景,展示了这种尝试及其遇到的问题:
假设我们有两个父实体Parent1和Parent2,它们都嵌入了一个Common组件。同时,存在一个OtherEntity,它与Parent1和Parent2分别建立了@ManyToOne关系。现在,我们希望在Common嵌入式类中定义一个@OneToMany关系,使其指向OtherEntity,并尝试使用mappedBy属性。
// 父实体 Parent1
@Entity
public class Parent1 {
// ... 其他字段和方法
@Embedded
private Common common; // 嵌入 Common 组件
}
// 父实体 Parent2
@Entity
public class Parent2 {
// ... 其他字段和方法
@Embedded
private Common common; // 嵌入 Common 组件
}
// 其他实体 OtherEntity
@Entity
public class OtherEntity {
// ... 其他字段和方法
@ManyToOne
@JoinColumn(name="p_id_1") // 与 Parent1 的多对一关系
private Parent1 parent1;
@ManyToOne
@JoinColumn(name="p_id_2") // 与 Parent2 的多对一关系
private Parent2 parent2;
}
// 嵌入式类 Common
@Embeddable
public class Common {
// ... 其他字段和方法
// 尝试在此处使用 mappedBy="IT_DEPENDS" 来定义 @OneToMany 关系
@OneToMany(mappedBy="IT_DEPENDS") // <-- 这种用法是无效的
private OtherEntity other;
}在这种设计中,核心问题在于Common嵌入式类中@OneToMany(mappedBy="IT_DEPENDS")的用法。开发者希望能够像在普通实体中一样,通过mappedBy指定关系的维护方,但JPA规范对此有明确的限制。
根据JPA规范(Persistence for Java™ EE, Version 2.2,特别是2.7节 Embeddable Classes),对嵌入式类可以包含的关联关系类型有严格的规定:
An embeddable class (including an embeddable class within another embeddable class) that is contained within an element collection must not contain an element collection, nor may it contain a relationship to an entity other than a many-to-one or one-to-one relationship. The embeddable class must be on the owning side of such a relationship and the relationship must be mapped by a foreign key mapping.
这段规范的关键点在于:
mappedBy属性的含义是指定关系的非拥有方(inverse side)。当一个实体使用mappedBy时,它表明关系的拥有方在另一侧,由另一侧的实体负责维护关系的外键。然而,JPA规范明确要求嵌入式类必须是关系的拥有方。因此,嵌入式类不能使用mappedBy来定义非拥有方的关系。
从JPA的设计哲学来看,@Embeddable类旨在作为其拥有实体的组成部分,而不是一个独立的、拥有自身生命周期和复杂关联管理能力的实体。它的存在是为了将一组相关的属性逻辑上分组,并作为值类型嵌入到宿主实体中。
鉴于JPA规范的限制,如果在您的模型中Common组件确实需要拥有一个指向OtherEntity的@OneToMany关系,那么您需要重新评估Common的角色:
将Common升级为实体:如果Common需要管理复杂的关联关系(如@OneToMany),并且可能拥有独立的生命周期或业务逻辑,那么它可能不适合作为@Embeddable。将其设计为一个独立的@Entity可能是更合适的选择。这样,Common就可以像任何其他实体一样,定义@OneToMany关系并使用mappedBy。
// 如果 Common 成为一个实体
@Entity
public class CommonEntity {
@Id
private Long id; // 需要有主键
// ... 其他字段
@OneToMany(mappedBy="common") // 现在可以使用 mappedBy
private List<OtherEntity> others;
}
@Entity
public class OtherEntity {
// ...
@ManyToOne
@JoinColumn(name="common_id")
private CommonEntity common; // OtherEntity 拥有 CommonEntity 的外键
}
@Entity
public class Parent1 {
@OneToOne // 或 @ManyToOne,取决于具体业务逻辑
@JoinColumn(name="common_id")
private CommonEntity common;
}重新设计关系:如果Common必须保持为嵌入式类,那么它与OtherEntity的关系需要重新设计,使其符合JPA规范。例如,在您的原始示例中,OtherEntity已经通过@ManyToOne关系引用了Parent1和Parent2。如果OtherEntity需要通过Parent1或Parent2的Common组件来“分组”,那么这种分组逻辑应该在Parent1或Parent2实体层面进行管理,而不是通过Common反向引用。
在当前示例中,OtherEntity已经与Parent1和Parent2建立了@ManyToOne关系。这意味着OtherEntity拥有Parent1和Parent2的外键。如果需要从Parent1或Parent2(或其嵌入的Common组件)访问所有相关的OtherEntity,那么Parent1或Parent2可以定义一个@OneToMany关系,并由OtherEntity中的parent1或parent2字段来mappedBy。
@Entity
public class Parent1 {
@Id
private Long id;
@Embedded
private Common common;
// Parent1 可以拥有一个到 OtherEntity 的 OneToMany 关系
@OneToMany(mappedBy="parent1")
private List<OtherEntity> relatedOthers;
}
// Common 仍然是嵌入式类,不直接管理 OneToMany 关系
@Embeddable
public class Common {
// ... 不包含 OneToMany 关系
}在这种情况下,Common组件仍然是Parent1的一部分,但Parent1本身负责管理其与OtherEntity的@OneToMany关联。
理解JPA规范对于正确设计实体模型至关重要。嵌入式类(@Embeddable)作为值类型组件,其在关联映射方面受到严格限制。它们不能作为@OneToMany或@ManyToMany关系的参与方,并且对于允许的@ManyToOne或@OneToOne关系,它们必须是拥有方,且不能使用mappedBy属性。
当您在设计中遇到需要在嵌入式类中定义复杂关联(尤其是@OneToMany并使用mappedBy)的需求时,这通常是一个信号,表明该组件可能更适合作为一个独立的实体存在,或者需要重新审视和调整您的实体关系模型,以符合JPA规范和最佳实践。
以上就是JPA/Hibernate嵌入式类中mappedBy属性的限制与原理分析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号