在 Doctrine ORM 2 中加入类表继承时出现“主键 elementId 的缺失值”

Posted

技术标签:

【中文标题】在 Doctrine ORM 2 中加入类表继承时出现“主键 elementId 的缺失值”【英文标题】:"Missing value for primary key elementId" when joining on Class Table Inheritance in Doctrine ORM 2 【发布时间】:2018-01-31 15:40:54 【问题描述】:

当我尝试加入继承的类时,我从 Doctrine 收到 OutOfBoundsException 并显示上面的错误消息。

我定义了以下实体:

FormElement 是父类

/**
 * @ORM\Entity()
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="ETYP_ID", type="integer")
 * @ORM\DiscriminatorMap(
 *     1 = "DatetimeElement",
 *     3 = "ChoiceElement",
 *     4 = "TextElement",
 *     5 = "MatrixElement",
 *     6 = "htmlElement"
 *     )
 * @Table(name="FORMELEMENT")
 */
abstract class Formelement 
    /**
     * @var integer
     * @ORM\Id()
     * @ORM\Column(name="ELE_ID", type="integer", nullable=false)
     */
    private $elementId;

ChoiceElement 是一个子类

/**
 * @ORM\Entity()
 * @ORM\Table(name="CHOICEELEMENT")
 */
class ChoiceElement extends Formelement 
    /**
     * @var integer
     * @ORM\Id()
     * @ORM\Column(name="CHOE_ID", type="integer", nullable=false)
     */
    private $id;
    /**
     * @var Choice[]|ArrayCollection
     * @ORM\OneToMany(targetEntity="Choice", mappedBy="choiceElement")
     */
    private $choices;

    public function getChoices()
        return $this->choices;
    

Choice 在 ChoiceElement 上加入

/**
 * Class Choice
 * @package apps\dynfrm\models\Doctrine\entities
 * @ORM\Entity()
 * @ORM\Table(name="CHOICE")
 */
class Choice 
    /**
     * @var integer
     * @ORM\Id()
     * @ORM\Column(name="CHO_ID", type="integer", nullable=false)
     */
    private $id;

    /**
     * @var ChoiceElement
     * @ORM\ManyToOne(targetEntity="ChoiceElement", inversedBy="choices")
     * @ORM\JoinColumn(name="CHOE_ID", referencedColumnName="CHOE_ID", nullable=false)
     */
    private $choiceElement;

ChoiceElement 是一个FormElement 并且有多个Choices。一切正常,即使是对ChoiceElement::getChoices() 的调用。但是,当我尝试访问生成的 ArrayCollection 时,Doctrine 会抛出上述错误。 我已经使用调试器进行了一些挖掘,但我不明白这是一个错误还是预期的行为。

我真的希望有人可以在这里帮助我。

【问题讨论】:

【参考方案1】:

由于ChoiceElement继承了Formelement,所以不需要另外有id字段。

事实上,当学说偶然发现您的ChoiceElement 实体时,它将定义一个复合主键,因为两个字段都标有@ORM\Id 注释,这可能不是您想要的行为。因此,当它尝试从您的 Choice 实体加入时,Doctrine 会抱怨,因为它只有组成主键的两个必需键之一。

要解决此问题,只需删除 ChoiceElement 实体的 id 属性即可。因此,不要忘记更新choiceElement 关联的referencedColumnName 属性。现在应该是ELE_ID 而不是CHOE_ID


编辑:快速工作示例:

AbstractA.php

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="integer")
 * @ORM\DiscriminatorMap(1 = "A")
 */
abstract class AbstractA


    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

A.php

/** @ORM\Entity() */
class A extends AbstractA

    /**
     * @ORM\OneToMany(targetEntity="B", mappedBy="a")
     */
    private $bs;

B.php

/** @ORM\Entity() */
class B

    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="A", inversedBy="bs")
     */
    private $a;

输出以下架构:

CREATE TABLE abstract_a (id INT AUTO_INCREMENT NOT NULL, discr INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE a (id INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE b (id INT AUTO_INCREMENT NOT NULL, a_id INT DEFAULT NULL, INDEX IDX_71BEEFF93BDE5358 (a_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE a ADD CONSTRAINT FK_E8B7BE43BF396750 FOREIGN KEY (id) REFERENCES abstract_a (id) ON DELETE CASCADE;
ALTER TABLE b ADD CONSTRAINT FK_71BEEFF93BDE5358 FOREIGN KEY (a_id) REFERENCES a (id);

当我尝试从A 实体浏览B 实体的数组集合时,Doctrine 成功触发了以下查询:

SELECT t0.id AS id_1, t0.a_id AS a_id_2 FROM b t0 WHERE t0.a_id = ?

以同样的方式,如果我要求它加入B 表,它也不会大惊小怪。这是我加入a.bs时执行的查询:

SELECT a0_.id AS id_0, b1_.id AS id_1, a0_.discr AS discr_2, b1_.a_id AS a_id_3 
FROM a a2_ 
  INNER JOIN abstract_a a0_ ON a2_.id = a0_.id 
  INNER JOIN b b1_ ON a2_.id = b1_.a_id 
WHERE a0_.id = ?

【讨论】:

不幸的是,这也不起作用。因为在我的架构中,FORMELEMENTCHOICEELEMENT 是不同的表,ELE_IDFORMELEMENT 的主键,CHOE_IDCHOICEELEMENT 的主键,所以这些键是不同的。如果我尝试在ELE_ID 上加入CHOE_ID,我不会得到任何结果。仅删除 Choiceelement 上的 @Id 注释也不起作用,我仍然收到相同的错误消息。 这很奇怪,我的一个项目中有一个非常相似的案例,它工作正常。我说的不是删除@Id 注释,而是从ChoiceElement 中删除整个$id 属性。在您的架构中是否有两个不同的表并不重要。 Doctrine 足够聪明,可以在子表中重复抽象类中定义的主键。这就是JOINED 策略的作用。你到底尝试了什么? @lioxo 我添加了一个对我有用的最小示例,希望它可以提供帮助。 谢谢,你的例子帮了大忙!不幸的是,它仍然不是我想要的。我按照你说的做了,从 ChoiceElement 中删除了$id-Attribute,并将 Choice 中的referencedColumnName 设置为ELE_ID。它现在不会抛出错误,但对于任何给定的ChoiceElement,它也不会返回任何Choices。 @lioxo 你在执行什么样的查询?

以上是关于在 Doctrine ORM 2 中加入类表继承时出现“主键 elementId 的缺失值”的主要内容,如果未能解决你的问题,请参考以下文章

Symfony/Doctrine 类表继承和外键作为主键

教义2中的类之间的多个CTI(类表继承)?

Doctrine 2 多重映射继承

doctrine2中的三级继承不加载二级数据。

Doctrine 2.2 ORM:查找扩展实体

学说(连接)类表继承中根实体的查询字段