6

Doctrine2 で Symfony2 を使用しています。私のプロジェクトでは、異なる関連マッピングを持つエンティティを作成しました。最初に、1 つのオブジェクトを要求するために約 7 つのクエリを確認したので、「eager-loading」を行うことにし、3 つのクエリに減らしました。

しかし、symfony ツールバー (Profiler) では、それらのうちの 2 つが互いに直接呼び出されて同じように見えます。私の理解では、コードに 3 番目のクエリは必要ありません。

では、doctrine php ファイルのどこにブレークポイントを設定して、コードのどの行で doctrine が新しいクエリを呼び出しているかを確認する必要がありますか? または、このリクエストを最適化する方法を確認する別の解決策はありますか?

アップデート:

Artworkad の回答について考えた後、さらに詳しく説明する必要があります。これは、Controller を介して 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 はIdentity Mapパターンを使用してオブジェクトを追跡します。したがって、データベースからオブジェクトを取得するたびに、Doctrine はこのオブジェクトへの参照を UnitOfWork 内に保持します。そして基本的に、ID をキーとして使用して UnitOfWork 内のオブジェクトを管理します。

例えば

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

データベースに対して SELECT クエリを 1 つだけ起動します。2 番目の呼び出しでは、doctrine は ID マップをチェックし、データベースのラウンドトリップを行わずに同じ ID を見つけます。プロキシ オブジェクトを使用しても、オブジェクトは同じ ID になります。

しかし、

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

同じオブジェクトを参照しているにもかかわらず、SQL ログに 2 つのクエリが表示されます。Doctrine は ID によってのみオブジェクトを認識しているため、別の基準のクエリは、以前に実行された場合でもデータベースに移動する必要があります。

しかし、doctrine はスマートです。新しいエンティティを作成するのではなく、ID を取得して、既にメモリ内にあるかどうかを調べます。


PHP はコピー オン ライト パラダイムに従います。これは最適化の原則です。変数の実際のコピーは、変数が変更されたときにのみ作成されます。したがって、データベースからオブジェクトを読み取るリクエストのメモリ使用量は、変数のコピーを保持しない場合と同じです。

したがって、変数を変更した場合にのみ、アプリケーションは内部で新しい変数を作成し、メモリを消費します。

したがって、flushを呼び出すと、doctrine は Identiy Map を繰り返し処理し、各オブジェクトの元のプロパティを現在の値と比較します。変更が検出されると、UPDATE クエリのキューに入れられます。実際に更新されたフィールドのみがデータベースで変更されます。

最適化する方法

そのため、オブジェクトを読み取り専用 (挿入と削除のみ) としてマークして、変更セットに含まれないようにすることが理にかなっている場合があります (xml マッピング ファイル、注釈、または php コードで行うことができます)。

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

または、1 つのエンティティのみをフラッシュします

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