教义:如何以编程方式启用急切加载?
Posted
技术标签:
【中文标题】教义:如何以编程方式启用急切加载?【英文标题】:Doctrine: how to enable eager loading programmatically? 【发布时间】:2016-07-04 18:48:57 【问题描述】:想象以下情况。你有书:
Book(bookId, authorId, title)
和作者:
Author(authorId, name)
每本书都有(为了简单起见)一个作者。
默认情况下,所有关联都配置为 lazy 模式。因此,如果我有这样的场景,当我第一次加载所有书籍、遍历集合并获取每本书的作者时,我将对数据库执行大量查询。
$books = $this->getDoctrine()
->getRepository('AppBundle:Book')
->findAll();
foreach($books as $b)
echo $b->getAuthor()->getName();
我可以以编程方式要求 Doctrine 为这个特定查询急切地加载作者吗(不是通过配置全局)?
相关: In Doctrine 2 can the Fetch Mode (Eager/Lazy etc.) be changed at runtime?
相关: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/getting-started.html#list-of-bugs
【问题讨论】:
最简单的方法是加入作者关系,最好通过在 Book 存储库中创建自己的“findAllWithAuthors”方法或类似方法。我在这里写了一个类似问题的答案***.com/questions/36250248/… @JimL Omg...在 Doctrine 中真的很难做到吗?! 我不会真的称之为难。在存储库中进行查询只是被认为是最佳实践。您也可以轻松地在控制器中创建查询。标准的简单方法(find、findby 等)通常只是为了帮助您入门。很少有人会在任何真正的应用程序/领域逻辑中坚持下去。$books = $this->getDoctrine()->createQueryBuilder()->select(['b', 'a'])->from('AppBundle:Book', 'b')->join('b.authors', a')->getQuery()->getResult();
或在 BookRepository 方法中 return $this->_em->createQueryBuilder('b')->addSelect('a')->join('b.authors', 'a')->getQuery->getResult();
【参考方案1】:
您可以简单地将书籍和作者之间的mark the association 设置为EAGER
(相对于LAZY
的隐含默认值),Doctrine 将始终预先加载该特定关联。
这可以通过添加:
fetch=EAGER
到映射关联。
在运行时执行此操作的一种可能方法是创建Mapped Superclass。超类将定义您的关系和关联的其他部分(而不是您尝试调整的关系)。
然后,要在运行时实际使用该类,您可以创建另外两个具体实现:LazyBook
和EagerBook
。根据您在运行时的场景,您将使用这些具体实现实体中的一个或另一个来构建您的关联。
当然,LazyBook
会将您的 Book
-> Author
关联定义为 LAZY
一个(显式或隐式),EagerBook
会将其定义为 EAGER
。
这不是您定义的真正动态的,但它允许您以编程方式确定在任何给定时间使用哪个关联,同时还可以自行记录它可能是。 p>
【讨论】:
我特别询问程序化方式,以便更灵活地处理查询。但是感谢您提供此选项! @DenisKulagin 嗯,好的。我想我并没有真正看到“在软件中编写”和“编程”之间的区别。他们没有实现相同的目标吗?或者目标是拥有一个要么 LAZY 或EAGER 的关联?如果那是目标,我也不理解用例。 是的,不同之处在于您无法在运行时切换。实际上,这不是关于用例,而是更多关于灵活性。我喜欢调整每个查询,以便它加载所需的确切数据量。不多,但也不少。 @DenisKulagin 我添加了一些关于映射超类的信息,这些信息可能会触及所有重要点(主要是在运行时决定)。【参考方案2】:这里要理解的一件非常重要的事情是,Doctrine 使用 Data Mapper 模式,而不是 Active Record 模式(例如,您可以在 Yii 框架中找到它):
Doctrine 2 是 php 5.4+ 的对象关系映射器 (ORM) 为 PHP 对象提供透明的持久性。 它使用数据 映射器模式在心脏,旨在完全分离您的 来自关系数据库中的持久性的域/业务逻辑 管理系统。
Doctrine 对程序员的好处是能够专注于 面向对象的业务逻辑和担心持久性只是 次要问题。这并不意味着持久性被低估了 教义 2,但是我们相信有相当多的 如果持久性和实体,面向对象编程的好处 保持分开。
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/getting-started.html#what-is-doctrine
这实质上意味着,实体类 对它们如何持久保存到数据库一无所知。尽管它们可以有注释类型的注释,但它们只是 ORM 处理的元数据的一种形式。
反过来,这意味着您可以执行与 ActiveRecord 相同的操作,但现在可以在另一个地方完成。我们来看看有什么区别:
在基于 ActiveRecord 的 ORM(如 Yii)中:
$books = Book::model()->with('author')->findAll();
在基于 DataMapper 的 ORM(如 Symfony/Doctrine)中:
$books = $this->getDoctrine()->createQueryBuilder()
->select(['b', 'a'])
->from('AppBundle:Book', 'b')
->join('b.author', a')
->addSelect('a')
->getQuery()
->getResult();
后面的小评论。您在那里构建的查询不是SQL 查询,而是DQL 查询(Doctrine 采用的对象查询语言)。
所以这里的 join/addSelect 很像前一个查询中的 with 只是告诉 ORM 引擎你想加载 author同一个查询。特定的关系元数据(例如,两个基础表的列名)仍然在实体元数据级别定义。
语法(select、from、join)类似于 SQL 故意,但您不应该被它弄糊涂。在这里,构建查询时,您操作的是 ORM 实体,而不是数据库列/表。
【讨论】:
以上是关于教义:如何以编程方式启用急切加载?的主要内容,如果未能解决你的问题,请参考以下文章