【问题标题】:Doctrine2 in Symfony2: How can I see which object-call leads into a query?Symfony2 中的 Doctrine2:如何查看哪个对象调用导致查询?
【发布时间】:2013-03-01 08:06:16
【问题描述】:

我将 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 与属性有关联。所以我用标准的[doctrine2关联映射(http://docs.doctrine-project.org/en/latest/reference/association-mapping.htmlManyToOneOneToMany制作了Gebiet 1:n Gebietsektoren n:1 Sektor

我从分析器中列出的 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]

【问题讨论】:

    标签: php mysql debugging symfony doctrine-orm


    【解决方案1】:

    Doctrine 使用 Identity Map 模式来跟踪对象。因此,每当您从数据库中获取对象时,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)
    

    【讨论】:

    • +1 为您的答案,因为它对我的理解有很大帮助!但我不确定这是否能解决我的具体问题:) 也许我必须编辑我的问题......
    • @Neysor 所有关联都加载到内存中。如果您不想这样做,则必须将关联标记为额外懒惰。因此,每次关联调用都会执行一个新查询。类似这里stackoverflow.com/questions/15101087/…
    • 我会看一下,但有 2 个相同的查询?看看我上次的编辑,我列出了查询...
    • @Neysor 根据您的代码,它应该只有一个查询。你确定你只查询数据库一次吗?也许你检查 sql 日志,而不仅仅是 profiler
    • 最后,您的帖子帮助我解决了我的问题并了解了学说 2 如何处理查询。所以我把你标记为我的答案!谢谢你的帮助!我真的需要它:)
    猜你喜欢
    • 2013-12-23
    • 1970-01-01
    • 1970-01-01
    • 2015-07-29
    • 2010-11-17
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多