6

我将 Symfony2 与 Doctrine2 一起使用。对于我的项目,我制作了具有不同关联映射的实体。首先,我确实看到了大约 7 个请求一个对象的查询,所以我决定进行“急切加载”并将它减少到三个查询。

但是其中两个在symfony工具栏(Profiler)中看起来是一样的,直接相互调用。据我了解,我的代码中不需要第三个查询。

那么我必须在哪里设置我的断点在教义 php 文件中,以查看我的代码的哪一行使教义调用新查询?还是有另一种解决方案可以查看我如何优化此请求?

更新:

在考虑了 Artworkad 的答案之后,我必须更详细地进行说明。这是因为我没有通过我的控制器发出 2 个对象请求。但也许它与我的树枝有关?

我的控制器

public function gebietAction($gebiet){
        $em = $this->getDoctrine()->getEntityManager();
        /* @var $gebietobj Gebiet */
        $gebietobj = $em->getRepository('ACGSigwxBundle:Gebiet')->findOneBy(array('short' => $gebiet));
        if (!$gebietobj) {
            throw $this->createNotFoundException('Kann das angegebene Gebiet nicht finden!');
        }
        return $this->render('ACGSigwxBundle:Sigwx:sigwx.html.twig',array("gebiet"=>$gebietobj));
    }

我的树枝模板

{% extends "ACGSigwxBundle::layout.html.twig" %}

{% block content %}
    <h1>{{ gebiet.getName() }}</h1>
    <p>My sectors:</p>
    <ul>
    {% for gs in gebiet.getGebietssektoren() %}
        <li>{{ gs.getSektor().getName() }}</li>
    {% endfor %}
    </ul>
{% endblock %}

对象关联

Gebiet n:n Sektor与属性有关联。所以我Gebiet 1:n Gebietsektoren n:1 Sektor用标准的[doctrine2关联映射(http://docs.doctrine-project.org/en/latest/reference/association-mapping.htmlManyToOneOneToMany

我从探查器中列出的 3 个查询

SELECT t0.id AS id1, t0.name AS name2, t0.short AS short3, t0.parent_id AS parent_id4 FROM gebiet t0 WHERE t0.short = ? LIMIT 1 Parameters: [app]

SELECT t0.id AS id1, t0.position AS position2, t0.size AS size3, t0.gebiet_id AS gebiet_id4, t0.sektor_id AS sektor_id5, t6.id AS id7, t6.name AS name8, t6.typ AS typ9, t6.erweitert AS erweitert10, t6.sortorder AS sortorder11 FROM gebietssektoren t0 INNER JOIN sektor t6 ON t0.sektor_id = t6.id WHERE t0.gebiet_id = ? Parameters: [1]

SELECT t0.id AS id1, t0.position AS position2, t0.size AS size3, t0.gebiet_id AS gebiet_id4, t0.sektor_id AS sektor_id5, t6.id AS id7, t6.name AS name8, t6.typ AS typ9, t6.erweitert AS erweitert10, t6.sortorder AS sortorder11 FROM gebietssektoren t0 INNER JOIN sektor t6 ON t0.sektor_id = t6.id WHERE t0.gebiet_id = ? Parameters: [1]
4

1 回答 1

7

Doctrine 使用身份映射模式来跟踪对象。因此,每当您从数据库中获取对象时,Doctrine 都会在其 UnitOfWork 中保留对该对象的引用。基本上,它使用 ID 作为管理 UnitOfWork 内部对象的键。

例如

$objectA = $this->entityManager->find('EntityName', 1);
$objectB = $this->entityManager->find('EntityName', 1);

只会对数据库触发一个 SELECT 查询。在第二次调用中,学说将检查身份映射,并在不进行数据库往返的情况下找到相同的 ID。即使您使用代理对象,该对象也将具有相同的 ID。

但对于

$objectA = $repository->findOneBy(array('name' => 'Benjamin'));
$objectB = $repository->findOneBy(array('name' => 'Benjamin'));

尽管您引用了同一个对象,但您会在 SQL 日志中看到两个查询。Doctrine 仅通过 ID 知道对象,因此对于不同条件的查询必须转到数据库,即使它之前已执行过。

但是学说很聪明,它不会创建新实体,而是获取 ID 并查看它是否已经在内存中。


PHP 遵循写时复制范式,这是一种优化原则。只有在修改变量时才会生成变量的真实副本。因此,从数据库读取对象的请求的内存使用量与不保留变量副本的内存使用量相同。

因此,只有当您更改变量时,您的应用程序才会在内部创建新变量并消耗内存。

因此,当您调用flush时,原则会遍历身份映射并将每个对象的原始属性与当前值进行比较。如果检测到更改,它将排队等待 UPDATE 查询。只有实际更新的字段在数据库中被更改。

如何优化

因此,有时将对象标记为只读(仅插入和删除)是有意义的,因此它们不会在变更集中(您可以在 xml 映射文件或使用注释或在您的 php 代码中执行此操作)。

$entityManager->getUnitOfWork()->markReadOnly($entity)

或仅刷新一个实体

$entityManager->flush($entity)
于 2013-03-01T09:17:04.093 回答