什么是inversedBy和mappedBy的区别?

124

我正在使用Zend Framework 2和Doctrine 2开发我的应用程序。

在编写注解时,我无法理解和之间的区别。

何时应该使用?

何时应该使用?

何时不需要使用它们?

以下是一个示例:

 /**
 *
 * @ORM\OneToOne(targetEntity="\custMod\Entity\Person", mappedBy="customer")
 * @ORM\JoinColumn(name="personID", referencedColumnName="id")
 */
protected $person;

/**
 *
 * @ORM\OneToOne(targetEntity="\Auth\Entity\User")
 * @ORM\JoinColumn(name="userID", referencedColumnName="id")
 */
protected $user;

/**
 *
 * @ORM\ManyToOne (targetEntity="\custMod\Entity\Company", inversedBy="customer")
 * @ORM\JoinColumn (name="companyID", referencedColumnName="id")
 */
protected $company;
我做了一个快速的搜索,发现了以下内容,但我仍然感到困惑:
- 示例1 - 示例2 - 示例3
4个回答

182

1
奇怪的是,Doctrine文档编写者决定省略yaml示例中的多对一双向映射,这可能是最常用的! - Peter Wooster
4
最佳做法是使用注释,因为这样可以将实体的所有信息放在一个地方! - Andreas Linden
这同样适用于多对多关系。对于这些关系,您可以自己选择多对多关联的拥有方。 - 11mb
5
@AndreasLinden 广泛使用并不代表最佳实践。使用注释来即时编写代码永远不能被认为是最佳实践,它不是PHP原生的,甚至在所有框架中都没有默认包含。将一个实体的所有信息集中在一个地方反而是一个反对意见。自从什么时候将所有代码分组到一个地方成为一件好事了?编写麻烦,维护困难,并且减少了项目的组织性。最佳实践? 呃。 - JesusTheHun
4
@JesusTheHun,你正在比较苹果和梨。“所有代码”与“有关实体的所有信息”非常非常不同;) - Andreas Linden
@JesusTheHun 我发现Symfony文档中很多决策令人困惑,但最明显和最有趣的是注释是开发者推荐的配置方式,而他们的示例默认为yml选项卡。 - SteveExdia

73

以上的答案对我来说不足以理解正在发生的事情,所以在深入研究后,我想我有一种解释方式可以让像我一样难以理解的人明白。

inversedBy和mappedBy是由内部教义引擎使用的,以减少必须执行的SQL查询数量,以获取所需的信息。要明确的是,如果您不添加inversedBy或mappedBy,则代码仍将工作,但不会被优化

例如,请查看下面的类:

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

    /**
     * @var string
     *
     * @ORM\Column(name="task", type="string", length=255)
     */
    private $task;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="dueDate", type="datetime")
     */
    private $dueDate;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="tasks", cascade={"persist"})
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    protected $category;
}

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

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="Task", mappedBy="category")
     */
    protected $tasks;
}
如果你运行生成架构的命令(例如:bin/console doctrine:schema:update --force --dump-sql),你会发现Category表上没有任务列(这是因为它在该列上没有注释)。需要注意的重要事项是,变量tasks只是为了让内部Doctrine引擎可以使用其上方的引用,该引用表示其映射到Category。此处不应混淆,Category并非指类名,而是指Task类中名为“$category”的属性。同样,在Tasks类中,属性$category指定为inversedBy="tasks",请注意这是复数形式,这并非类名的复数形式,而只是因为Category类中的属性称为“protected $tasks”。一旦理解了这一点,就很容易理解inversedBy和mappedBy的作用以及如何在此情况下使用它们。在我的示例中,引用外键的一侧始终会获得inversedBy属性,因为它需要知道哪个类(通过targetEntity命令)和该类上的哪个变量(inversedBy=)来“向后工作”,从而获取类别信息。一个记住这一点的简单方法是,将具有foreignkey_id的类是需要具有inversedBy属性的。至于Category和其$tasks属性(请记住,仅出于优化目的,该属性并未出现在表中),则使用MappedBy“tasks”,这将正式创建两个实体之间的关系,以便Doctrine现在可以安全地使用JOIN SQL语句而不是两个单独的SELECT语句。如果没有mappedBy,则Doctrine引擎将无法从它将创建的JOIN语句中知道将类别信息放入“任务”类中的哪个变量。希望这能解释得更清楚一些。

7
非常好地解释了,感谢你的努力。我从Laravel Eloquent转到Doctrine,这里的逻辑对我来说很难理解。做得好。 “Category不是指类名,而是指Task类中名为'protected $category'的属性。” 这就是我需要的全部。它不仅解决了我的问题,还帮助我理解了问题。我认为这是最好的答案 :-) - The Alpha
1
我也是从Eloquent来的,这对我帮助很大。我现在唯一困扰的是如何设置它的setter/getter,我还在学习中。 - Eman
1
是的,这真的很容易,甚至可以使用一些工具自动化完成。稍后与我开始聊天,我可以帮助你。 - Joseph Astrahan
1
请查看这个答案,https://dev59.com/9HLYa4cB1Zd3GeqPVkbg - Joseph Astrahan
1
http://symfony.com/doc/current/doctrine/associations.html,这其实是一个更好的学习方式,因为Symfony使用Doctrine,所以它会教你同样的东西。 - Joseph Astrahan
显示剩余7条评论

23

在双向关系中,既有拥有方(owning side)也有反转方(inverse side)。

mappedBy:用于将反向关系中的实体字段映射到拥有方实体的字段。

inversedBy:用于将拥有方关系中的实体字段映射到反转方实体的字段。

AND

mappedBy 属性用于 OneToOne、OneToMany 或 ManyToMany 映射声明。

inversedBy 属性用于 OneToOne、ManyToOne 或 ManyToMany 映射声明。

注意:在双向关系中,拥有方是包含外键的一方。

Doctrine 文档中有关于 inversedBy 和 mappedBy 的两个参考链接:第一个链接第二个链接


1
链接失效了吗? - Scaramouche

2

5.9.1. 拥有方和反向关系

对于多对多的关联关系,您可以选择哪个实体是拥有方和反向关系。从开发人员的角度来看,有一个非常简单的语义规则来决定哪一边更适合成为拥有方。您只需要问自己,哪个实体负责连接管理,并将其选为拥有方。

以 Article 和 Tag 两个实体为例。每当您想要将文章与标签进行连接时,大多数情况下是文章负责此关系。每当您添加新文章时,您都希望将其与现有或新标签进行连接。您的创建文章表单可能会支持这种想法并允许直接指定标签。这就是为什么您应该选择 Article 作为拥有方,因为它使代码更易懂:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接