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

时间:2023-01-13 06:44:40

I'm using Symfony2 with Doctrine2. For my project I made Entities with different association-mapping. First I did see about 7 queries for requesting one object, so i decided to make "eager-loading" and it reduced to three queryies.

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

But two of them looks to be the same in the symfony toolbar (Profiler) directly called after each other. In my understanding there is no need of a third query in my code.

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

So where do I have to set my breakpoints in the doctrine php files to see which line of my code makes doctrine calling a new query? Or is there another solution to see how i can optimize this requests?

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

Update:

After thinking about Artworkad answer, I have to go much more in detail. This is because I do not make 2 object request via my Controller. But perhaps it has something to do with my twig?

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

My Controller

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));
    }

My Twig Template

{% 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 %}

Object association

There is a association Gebiet n:n Sektor with attributes. So i made Gebiet 1:n Gebietsektoren n:1 Sektor with standard [doctrine2 association mappings(http://docs.doctrine-project.org/en/latest/reference/association-mapping.html) ManyToOne and OneToMany

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)ManyToOne和OneToMany

My 3 listed queries from profiler

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]

1 个解决方案

#1


6  

Doctrine uses the Identity Map pattern to track objects. So whenever you fetch an object from the database, Doctrine keeps a reference to this object inside its UnitOfWork. And basically it uses the ID as a key to manage objects inside its UnitOfWork.

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

E.g.

例如。

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

would fire only one SELECT query against the database. In the second call doctrine will check the identity map and would find the same ID without doing a database roundtrip. Even if you use proxy object, the object would have same ID.

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

But for

但对于

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

you would see two queries in your SQL log, despite the fact that you reference the same object. Doctrine only knows objects by ID, so a query for a different criteria has to go to the database, even if it was executed before.

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

But doctrine is smart, it does not create a new entity but gets the ID and looks if it is alrady in memory.

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


PHP follows the copy-on-write paradigm, it's a optimization principle. A real copy of a variable is only made when the variable is modified. So the memory usage for a request that reads objects from database is the same as if not to keep a variable copy.

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

So only when you change variables your applications creats new variables internally and consumes memory.

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

So when you call flush , doctrine iterates over the Identiy Map and compares each obecjts's original property with the current values. If changes are detected it will queue for an UPDATE query. Only actually updated fields are changed in database.

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

How to optimize

如何优化

So sometimes it makes sense to mark objects as read only (only insert and remove), so they will not be in the changeset (you can do it in your xml mapping file or with annotations or in your php code).

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

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

Or flush only one entity

或者只刷一个实体

$entityManager->flush($entity)

#1


6  

Doctrine uses the Identity Map pattern to track objects. So whenever you fetch an object from the database, Doctrine keeps a reference to this object inside its UnitOfWork. And basically it uses the ID as a key to manage objects inside its UnitOfWork.

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

E.g.

例如。

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

would fire only one SELECT query against the database. In the second call doctrine will check the identity map and would find the same ID without doing a database roundtrip. Even if you use proxy object, the object would have same ID.

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

But for

但对于

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

you would see two queries in your SQL log, despite the fact that you reference the same object. Doctrine only knows objects by ID, so a query for a different criteria has to go to the database, even if it was executed before.

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

But doctrine is smart, it does not create a new entity but gets the ID and looks if it is alrady in memory.

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


PHP follows the copy-on-write paradigm, it's a optimization principle. A real copy of a variable is only made when the variable is modified. So the memory usage for a request that reads objects from database is the same as if not to keep a variable copy.

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

So only when you change variables your applications creats new variables internally and consumes memory.

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

So when you call flush , doctrine iterates over the Identiy Map and compares each obecjts's original property with the current values. If changes are detected it will queue for an UPDATE query. Only actually updated fields are changed in database.

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

How to optimize

如何优化

So sometimes it makes sense to mark objects as read only (only insert and remove), so they will not be in the changeset (you can do it in your xml mapping file or with annotations or in your php code).

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

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

Or flush only one entity

或者只刷一个实体

$entityManager->flush($entity)