Quarkus Hibernate中含附加属性的多对多关联映射问题
解决Quarkus中带附加属性的多对多关联映射问题
问题根源
触发空指针异常的核心原因是Hibernate不允许在@Embeddable复合主键类中直接使用@ManyToOne实体关联。另外,继承GenericEntity会引入PanacheEntity的自动整型主键,不符合复合主键的需求,因此中间实体应继承PanacheEntityBase(无自动主键生成逻辑)。
修正方案
1. 修改复合主键类(CorporationHasAddressKey)
将实体关联替换为基本类型的外键字段,复合主键仅支持基本类型或可序列化类型:
@Embeddable public class CorporationHasAddressKey implements Serializable { // 对应CorporationEntity的主键(与PanacheEntity的id类型保持一致) public Long corporationId; // 对应AddressEntity的主键 public Long addressId; // JPA强制要求:实现equals和hashCode用于主键比较 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CorporationHasAddressKey that = (CorporationHasAddressKey) o; return Objects.equals(corporationId, that.corporationId) && Objects.equals(addressId, that.addressId); } @Override public int hashCode() { return Objects.hash(corporationId, addressId); } }
2. 修正中间关联实体(CorporationHasAddressEntity)
使用@MapsId将实体关联映射到复合主键的对应字段,同时保留PanacheEntityBase继承:
@Entity public class CorporationHasAddressEntity extends PanacheEntityBase { @EmbeddedId public CorporationHasAddressKey id; @ManyToOne @MapsId("corporationId") // 将关联的主键同步到复合主键的corporationId字段 @JoinColumn(name = "corporation_id") public CorporationEntity corporation; @ManyToOne @MapsId("addressId") // 将关联的主键同步到复合主键的addressId字段 @JoinColumn(name = "address_id") public AddressEntity address; @ManyToOne @JoinColumn(name = "addressType_id") public AddressTypeEntity addressType; // 若需要复用公共字段,直接添加(或调整GenericEntity继承PanacheEntityBase后复用) public LocalDate createdDate; public LocalDate lastRevision; }
3. 调整主实体(可选,简化关联操作)
在CorporationEntity和AddressEntity中添加对中间实体的集合关联:
@Entity public class CorporationEntity extends GenericEntity { @ManyToOne public BuildingEntity airport; @ManyToOne public CustomerEntity customer; @OneToMany(mappedBy = "corporation", cascade = CascadeType.ALL, orphanRemoval = true) public List<CorporationHasAddressEntity> addressRelations = new ArrayList<>(); // 其他属性... }
// 示例AddressEntity结构 @Entity public class AddressEntity extends GenericEntity { // 其他字段... @OneToMany(mappedBy = "address", cascade = CascadeType.ALL, orphanRemoval = true) public List<CorporationHasAddressEntity> corporationRelations = new ArrayList<>(); }
4. 可选:优化公共基类(GenericEntity)
若要复用公共字段且不引入自动主键,修改GenericEntity继承PanacheEntityBase,让所有实体手动定义主键:
@MappedSuperclass public class GenericEntity extends PanacheEntityBase { public LocalDate createdDate; public LocalDate lastRevision; }
之后CorporationEntity、AddressEntity等可继承该类,并通过@Id+@GeneratedValue自定义主键策略。
内容的提问来源于stack exchange,提问作者Nico Foth
相关产品推荐
相关产品推荐

