Symfony5+Doctrine2嵌套复合外键作为实体ID无法生效问题咨询
Doctrine 2 不支持直接将带复合主键的实体作为另一个实体的复合主键组成部分,该限制属于ORM核心层,并非迁移生成工具的问题,即使手动建表也无法正常运行,ORM在解析关联、生成查询SQL时会默认尝试读取关联实体的单字段id,因此会抛出你遇到的报错。
以下是两种可行的适配方案:
方案一:显式拆分复合主键字段(保留原有数据库键设计)
将OrderItem的两个主键字段直接拆入OrderItemParticipation的主键中,额外维护一个只读的orderItem关联属性即可,映射代码如下:
// 原有OrderItem映射无需修改 /** @ORM\Entity */ class OrderItemParticipation { /** * @ORM\Id * @ORM\ManyToOne(targetEntity=Order::class) */ private Order $order; /** * @ORM\Id * @ORM\ManyToOne(targetEntity=Product::class) */ private Product $product; /** * @ORM\Id * @ORM\ManyToOne(targetEntity=Participant::class) */ private Participant $participant; /** * @ORM\ManyToOne(targetEntity=OrderItem::class, inversedBy="participations") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="order_id", referencedColumnName="order_id", insertable=false, updatable=false), * @ORM\JoinColumn(name="product_id", referencedColumnName="product_id", insertable=false, updatable=false) * }) */ private OrderItem $orderItem; public function __construct(OrderItem $orderItem, Participant $participant) { $this->order = $orderItem->getOrder(); $this->product = $orderItem->getProduct(); $this->orderItem = $orderItem; $this->participant = $participant; } // 省略其他getter、setter逻辑 }
该方案完全保留原有数据库表结构,主键仍为order_id + product_id + participant_id的复合键,对象层面也可正常通过$participation->getOrderItem()访问关联的订单项,不会破坏领域模型的封装。
方案二:给OrderItem增加独立单主键(简化后续关联逻辑)
如果没有强制要求必须使用复合主键,给OrderItem新增一个自增单字段主键是更省心的方案,后续所有关联OrderItem的场景都无需处理复合键逻辑,代码维护成本更低,映射调整如下:
/** @ORM\Entity * 新增唯一约束保证同一个订单下同一个商品不会重复生成订单项 * @ORM\Table(uniqueConstraints={ * @ORM\UniqueConstraint(columns={"order_id", "product_id"}) * }) */ class OrderItem { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private int $id; /** * @ORM\ManyToOne(targetEntity=Order::class, inversedBy="items") */ private Order $order; /** * @ORM\ManyToOne(targetEntity=Product::class) */ private Product $product; // 省略其他逻辑 } // OrderItemParticipation映射无需修改即可正常运行 /** @ORM\Entity */ class OrderItemParticipation { /** * @ORM\Id * @ORM\ManyToOne(targetEntity=OrderItem::class, inversedBy="participations") */ private OrderItem $orderItem; /** * @ORM\Id * @ORM\ManyToOne(targetEntity=Participant::class) */ private Participant $participant; // 省略其他逻辑 }
该方案符合Doctrine的常规设计习惯,迁移生成、查询、关联操作都不会出现兼容问题。
内容的提问来源于stack exchange,提问作者Subsurf
相关产品推荐
相关产品推荐

