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

JPA:父实体含两个子实体时,将子实体关联到单一父实体

解决方案

你可以通过以下几种方式解决这个JPA关联冗余的问题:

方法一:使用单向OneToOne关联

直接移除ShipmentAddress中的反向关联属性,仅在Shipment中维护正向的一对一关系。这种方式最简单,适合不需要从ShipmentAddress反向查询所属Shipment的场景。

修改后的ShipmentAddress类:

@Entity
public class ShipmentAddress {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable = false)
    private String street1;
    private String street2;

    // 移除所有指向Shipment的属性,仅保留地址相关字段
}

Shipment类保持原有代码不变,所有关联操作从Shipment端发起即可。

方法二:通过继承+鉴别器区分地址类型

将ShipmentAddress抽象为父类,创建两个子类分别对应发货地址和收货地址,父类中仅保留一个指向Shipment的关联属性,子类通过鉴别器字段区分类型。

  1. 抽象父类ShipmentAddress:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "address_type", discriminatorType = DiscriminatorType.STRING)
public abstract class ShipmentAddress {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable = false)
    private String street1;
    private String street2;

    @OneToOne
    private Shipment shipment;
}
  1. 发货地址子类FromAddress:
@Entity
@DiscriminatorValue("FROM")
public class FromAddress extends ShipmentAddress {
    // 无需额外字段,继承父类所有属性
}
  1. 收货地址子类ToAddress:
@Entity
@DiscriminatorValue("TO")
public class ToAddress extends ShipmentAddress {
    // 无需额外字段,继承父类所有属性
}
  1. 修改Shipment类关联子类:
@Entity
@Table(name = "shipment")
public class Shipment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private AppUser user;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "shipment")
    private FromAddress fromAddress;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "shipment")
    private ToAddress toAddress;
}

方法三:添加地址类型标记+过滤关联

在ShipmentAddress中添加一个枚举字段标记地址类型(发货/收货),使用@Where注解在Shipment中过滤出对应类型的地址,同时仅保留一个指向Shipment的关联属性。

  1. 定义地址类型枚举:
public enum AddressType {
    FROM, TO
}
  1. 修改ShipmentAddress类:
@Entity
public class ShipmentAddress {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable = false)
    private String street1;
    private String street2;
    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private AddressType type;

    @ManyToOne
    private Shipment shipment;
}
  1. 修改Shipment类添加过滤条件:
@Entity
@Table(name = "shipment")
public class Shipment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private AppUser user;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "from_address_id")
    @Where(clause = "type = 'FROM'")
    private ShipmentAddress fromAddress;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "to_address_id")
    @Where(clause = "type = 'TO'")
    private ShipmentAddress toAddress;
}

内容的提问来源于stack exchange,提问作者Johnny boy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:40:21