我有一个Car
实体与实体Owner
有多对一的关系。如果我选择所有汽车,Doctrine会在Car
表上执行一次查询,然后在每个汽车的Owner
表上执行一次查询。因此,获取N个汽车将成为N + 1个查询,而不是Car
和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查询呢?
答案 0 :(得分: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();
答案 1 :(得分: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(通常更快,因为它需要更少的db查询,具体取决于记录的数量)。
现在启动foreach
,Doctrine会在内部找到实体,当您需要owner
时,它不会运行单个查询。
在每次更改后首先/之后监控查询数量(例如,mysql常规日志)
答案 2 :(得分:1)
您的查询...
$car->getOwner() // "go and fetch this car's owner"
...在foreach循环中,所以肯定会多次发出查询。
如果您正在编写自定义DQL来处理此问题,那么$car->getOwner()
就不应该具有此功能。这是Car类的功能。您要编写的自定义DQL将模仿您指出的确切SQL查询,并有效地完成连接。