You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 08:52:26