Doctrine2 多对一关联不会使用 JOIN 查询
Posted
技术标签:
【中文标题】Doctrine2 多对一关联不会使用 JOIN 查询【英文标题】:Doctrine2 many-to-one association won't use JOIN query 【发布时间】:2010-11-12 18:23:41 【问题描述】:我有一个 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 查询?
【问题讨论】:
【参考方案1】:至少在 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();
【讨论】:
看起来在 2.x 中也是如此。我试过查询SELECT c FROM Car c JOIN c.owner o
,它仍然进行了 N+1 个查询,但你的工作正常。非常感谢!我想我只能忍受不使用 findXxx 方法。
对于这样的方便方法,我建议创建一个“服务层”或“存储库”样式类来处理加载模型等任务。这样,您还可以轻松地重用查询,而不必到处复制 DQL 逻辑。【参考方案2】:
首先运行一个 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 常规日志)
【讨论】:
【参考方案3】:您的查询...
$car->getOwner() // "go and fetch this car's owner"
... 在 foreach 循环中,因此它肯定会多次发出查询。
如果您正在编写自定义 DQL 来处理这个问题,$car->getOwner()
根本不应该出现在其中。这是 Car 类的一个函数。您将编写的自定义 DQL 将模仿您指出的确切 SQL 查询并有效地完成您的联接。
【讨论】:
+1 感谢您的回复。只要所有者成员是 EAGERly 加载的,在循环内执行$car->getOwner()
就不是问题。如果这只是发生在延迟获取中,我会理解,但是通过急切加载它并不理想。请参阅我对 Jani Hartikainen 关于 DQL 的回复 :-)
@Frode:确实,现在更好地理解您的问题。还没有遇到过这种情况,并且希望从急切加载中获得与您相同的效率。看起来需要 DQL。以上是关于Doctrine2 多对一关联不会使用 JOIN 查询的主要内容,如果未能解决你的问题,请参考以下文章