8

我有一个Car与 entity 具有多对一关系的实体Owner。如果我选择所有汽车,Doctrine 会在Car表上进行一次查询,然后在Owner表上为每辆汽车进行一次查询。Car因此,获取 N 辆汽车变成了 N+1 个查询,而不是和Owner表之间的单个 JOIN 查询。

我的实体如下:

/** @Entity */
class Car {

  /** @Id @Column(type="smallint") */
  private $id;

  /** @ManyToOne(targetEntity="Owner", fetch="EAGER")
      @JoinColumn(name="owner", referencedColumnName="id") */
  private $owner;

  public function getId()    { return $this->id; }
  public function getOwner() { return $this->owner; }
}

/** @Entity */
class Owner {

  /** @Id @Column(type="smallint") */
  private $id;

  /** @Column(type="string") */
  private $name;

  public function getName() { return $this->name; }
}

如果我想列出汽车及其所有者,我会:

$repo = $em->getRepository('Car');
$cars = $repo->findAll();

foreach($cars as $car) 
  echo 'Car no. ' . $car->getId() . 
       ' owned by ' . $car->getOwner()->getName() . '\n';

现在这一切都运行良好,除了 Doctrine 为每辆车发出查询。

SELECT * FROM Car;
SELECT * FROM Owner WHERE id = 1;
SELECT * FROM Owner WHERE id = 2;
SELECT * FROM Owner WHERE id = 3;
....

当然,我希望我的查询日志看起来像这样:

SELECT * FROM Car JOIN Owner ON Car.owner = Owner.id;

不管我有fetch="EAGER"没有fetch="LAZY"关系,即使我在两个实体之间使用 JOIN 进行自定义 DQL 查询,$car->getOwner()仍然会导致 Doctrine 查询数据库(除非我使用 EAGER,在这种情况下$repo->findAll()会导致所有这些)。

我在这里是不是太累了,这就是它应该工作的方式 - 或者有没有一种聪明的方法来强制 Doctrine 执行 JOIN 查询?

4

3 回答 3

6

至少在 1.x Doctrine 中,如果要查询相关对象,则必须使用 DQL。对于您的情况,DQL 查询看起来像这样:

//Assuming $em is EntityManager
$query = $em->createQuery('SELECT c, o FROM Car c JOIN c.owner o');
$cars = $query->execute();
于 2010-11-12T18:45:45.317 回答
4

首先运行一个 DQL 查询,在该查询中选择与车主连接的所有汽车 (DQL JOIN)。把所有者放在select().

// preload cars
$qb = $em->createQueryBuilder()
        ->select('car, owner')
        ->from('\Entity\Car', 'car')
        ->leftJoin('c.owner',  'owner');

    $query = $qb->getQuery();

    // the following seems not needed, but I think it depends on the conf
    $query->setFetchMode("\Entity\Car", "owner", "EAGER");

    $query->execute(); //you don't have to use this result here, Doctrine will keep it

然后 Doctrine 2 将执行 JOIN(通常更快,因为它需要更少的数据库查询,具体取决于记录的数量)。现在启动你的foreach,Doctrine 会在内部找到实体,当你需要owner.

在每次更改之前/之后监控查询次数(例如 mysql 常规日志)

于 2013-09-03T11:00:36.883 回答
1

您的查询...

$car->getOwner() // "go and fetch this car's owner"

... 在 foreach 循环中,因此它肯定会多次发出查询。

如果您正在编写自定义 DQL 来处理此问题,$car->getOwner()则根本不应该在其中出现。这是 Car 类的一个函数。您将编写的自定义 DQL 将模仿您指出的确切 SQL 查询并有效地完成您的连接。

于 2010-11-12T19:18:46.987 回答