【问题标题】:Doctrine 2: Efficient way to query a many-to-many association原则 2:查询多对多关联的有效方法
【发布时间】:2016-01-23 15:10:08
【问题描述】:

我在两个表(franchise 和 player)之间有一个多对多关联,由一个额外的表(franchise_players)构建。 我已经创建了三个实体:

class Franchise 
{
    ...
    /**
     * 
     * @var ArrayCollection
     * 
     * @ORM\OneToMany(targetEntity="NBA\Entity\FranchisePlayer", mappedBy="franchise")
     */
    private $franchiseplayers;
    ...
}

class Player
{
    /* some fields */
}

class FranchisePlayers 
{
    ...
    /**
     * @var \NBA\Entity\Player
     *
     * @ORM\ManyToOne(targetEntity="NBA\Entity\Player")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="playerId", referencedColumnName="id")
     * })
     */
     private $player;

     /**
      * @var \NBA\Entity\Franchise
      *
      * @ORM\ManyToOne(targetEntity="NBA\Entity\Franchise")
      * @ORM\JoinColumns({
      *   @ORM\JoinColumn(name="franchiseId", referencedColumnName="id")
      * })
      */
      private $franchise;
}

我的目标是获得所有特许经营权及其相关球员。

显而易见的解决方案简单但效率不高:

Get all franchises
foreach $franchise
    $franchise->getFranchisePlayers()
    foreach $franquisePlayer
        $franquisePlayer->getPlayer()->getFullName();

另一种解决方案是获取所有特许经营权,然后执行左连接以获取每个特许经营权的相关玩家:

Get all franchises
foreach $franchise
    public function getCurrentPlayers(Franchise $franchise) {   

    $qb = $this->entityManager->createQueryBuilder();
    $qb->select(array('p'))
       ->from('NBA\Entity\Player', 'p')
       ->leftjoin('NBA\Entity\FranchisePlayer','f','WITH','f.player=p')
       ->where('f.franchise = ?1')
       ->orderBy('p.surname','ASC')
       ->setParameter(1,$franchise);

    $query = $qb->getQuery();
    return $query->getResult();
}

通过这种方式,我们减少了查询次数。

我的问题是我是否可以通过这样的一个查询来实现我的目标:

public function getFranchisesWithPlayers() {
    $qb = $this->entityManager->createQueryBuilder();
    $qb->select(array('f'))
        ->from('NBA\Entity\Franchise', 'f')
        ->leftJoin('NBA\Entity\FranchisePlayer','fp','WITH','fp.franchise=f')
        ->leftJoin('NBA\Entity\Player','p','WITH','fp.player=p')
        ->orderBy('f.name')
        ->addOrderBy('p.surname');

    $query = $qb->getQuery();
    return $query->getResult();     
}

我认为 Franchise 实体的 FranchisePlayers 字段(以及每个 FranchisePlayer 的 de Player 实体)会自动加载,但似乎并非如此。

有可能吗?也许我在实体的映射中做错了什么?

谢谢!

编辑: 昨天我确信查询越少效率最高,但现在我怀疑了。也许水合这个复杂查询的结果的成本大于水合选项 2 查询的每个结果的成本(在不同的查询中查询每个特许经营的球员)。你怎么看?

【问题讨论】:

    标签: doctrine-orm dql


    【解决方案1】:

    文档展示了如何创建查询。可能值得一看。比如:

    $qb->select('franchise,franchisePlayer,player')
        ->from('NBA\Entity\Franchise', 'franchise')
        ->leftJoin('franchise.franchisePlayers','franchisePlayer')
        ->leftJoin('franchisePlayer.player','player')
        ->orderBy('franchise.name')
        ->addOrderBy('player.surname');
    

    【讨论】:

    • 我已经检查了你的建议,但是这个查询返回了一个不同对象的数组。第一个元素是 Franchise,但第二个元素是 FranchisePlayer,第三个元素是 Player,第四个元素是 FranchisePlayer。也就是说,我在一个查询中就拥有了我想要的所有对象(太棒了!),但混合在同一个数组中。
    • 只是为了集中问题:我想获得一个 Franchise 数组,其中已经加载了franchisePlayers 字段(以避免额外的查询)。而且这些franchisePlayers 对象也应该加载了 player 字段。
    • 再次检查。所示查询将返回特许经营对象列表。
    • 谢谢你,你是完全正确的。我没有正确构建查询。您认为只执行一个查询更有效还是为每个特许经营执行一个查询以获得相关球员更好?
    • 对于这些类型的查询,当您知道需要所有附加信息时,通常最好使用单个查询。当您有多个 ManyToMany 关系时,事情可能会开始变慢。但每个案例都会有所不同。
    猜你喜欢
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    相关资源
    最近更新 更多