Symfony2中的Doctrine2:如何查看哪个对象调用导致查询?

Ney*_*sor 6 php mysql debugging symfony doctrine-orm

我正在使用Symfony2和Doctrine2.对于我的项目,我使用不同的关联映射创建了实体.首先,我确实看到了大约7个查询请求一个对象,所以我决定"急切加载",它减少到三个查询.

但是他们中的两个看起来在symfony工具栏(Profiler)中是相同的,直接相互调用.根据我的理解,我的代码中不需要第三个查询.

那么在哪里我必须在doctrine php文件中设置断点,看看我的代码哪一行让doctrine调用一个新的查询?或者是否有其他解决方案来了解我如何优化此请求?

更新:

在考虑了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));
    }
Run Code Online (Sandbox Code Playgroud)

我的树枝模板

{% 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 %}
Run Code Online (Sandbox Code Playgroud)

对象关联

Gebiet n:n Sektor与属性有关联.所以我Gebiet 1:n Gebietsektoren n:1 Sektor使用标准[doctrine2 association mappings(http://docs.doctrine-project.org/en/latest/reference/association-mapping.html)ManyToOneOneToMany

来自探查器的我列出的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]
Run Code Online (Sandbox Code Playgroud)

Upv*_*ote 7

Doctrine使用Identity Map模式跟踪对象.因此,无论何时从数据库中获取对象,Doctrine都会在其UnitOfWork中保留对此对象的引用.基本上它使用ID作为密钥来管理其UnitOfWork中的对象.

例如

$objectA = $this->entityManager->find('EntityName', 1);
$objectB = $this->entityManager->find('EntityName', 1);
Run Code Online (Sandbox Code Playgroud)

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

但对于

$objectA = $repository->findOneBy(array('name' => 'Benjamin'));
$objectB = $repository->findOneBy(array('name' => 'Benjamin'));
Run Code Online (Sandbox Code Playgroud)

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

但是,学说是聪明的,它不会创建一个新的实体,而是获取ID并查看它是否在内存中是alrady.


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

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

因此,当您调用flush时,doctrine会对Identiy Map进行迭代,并将每个obecjts的原始属性与当前值进行比较.如果检测到更改,它将排队进行UPDATE查询.仅在数据库中更改了实际更新的字段.

如何优化

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

$entityManager->getUnitOfWork()->markReadOnly($entity)
Run Code Online (Sandbox Code Playgroud)

或者只冲洗一个实体

$entityManager->flush($entity)
Run Code Online (Sandbox Code Playgroud)

  • 给你的答案+1,因为它对我的理解有很大的帮助!但我不确定这会解决我的具体问题:)也许我必须编辑我的问题... (2认同)