40

次のようなクエリがあります。

ユーザー エンティティには、次のような 1 対 1 の関係があります。

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user")
 */
private $userProfile;

複数のユーザー オブジェクトを選択するクエリを作成すると、get メソッドを使用してアクセスしていなくても、UserProfile データをクエリするためにユーザーごとに追加の select ステートメントが作成されます。UserProfile データが常に必要なわけではありません。また、ユーザーのリストを表示するたびにこのデータをロードしたくはありません。

これらのクエリが実行時に実行される理由は何ですか?

4

6 に答える 6

27

詳細を説明するソリューションは次のとおりです。

https://groups.google.com/forum/#!topic/doctrine-user/fkIaKxifDqc

マッピングの「フェッチ」はヒントです。つまり、可能であれば Doctrine はそれを行いますが、可能でない場合は明らかに行いません。遅延読み込みのプロキシは、技術的に常に可能であるとは限りません。不可能な状況は次のとおりです。

1) 逆から所有側への 1 対 1 (双方向の 1 対 1 関連付けでのみ表示されます)。上記の前提条件 a) を満たすことはできません。2) 階層への 1 対 1/多対 1 の関連付けで、ターゲット クラスにサブクラスがある (クラス階層のリーフではない)。上記の前提条件 b) を満たすことはできません。

このような場合、プロキシは技術的に不可能です。

この n+1 問題を回避するオプション:

1) DQL を介したフェッチ-ジョイン: "select c,ca from Customer join c.cart ca". クエリは 1 つですが結合しますが、1 対 1 の関連付けでの結合は比較的低コストです。

2) 部分オブジェクトを強制します。追加のクエリはありませんが、遅延読み込みもありません: $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)

3) 代替の結果フォーマット (つまり getArrayResult()) がユースケースに十分な場合、これらもこの問題を回避します。

Benjamin は、n+1 クエリを回避するために、これらのロードの自動バッチ処理についていくつかのアイデアを持っていましたが、プロキシが常に可能であるとは限らないという事実は変わりません。

于 2014-03-07T15:11:35.613 に答える
19

解決策を探すのに多くの時間を費やしました。私にとって、十分に満足できるオプションはありませんでしたが、次の回避策のリストで誰かの時間を節約できるかもしれません。

1) 所有側と逆側を変更するhttp://developer.happyr.com/choose-owning-side-in-onetoone-relation - DB 設計の観点から毎回正しいとは思いません。

2) 、 などの関数findfindAllは、OneToOne の逆側が自動的に結合されます (常に fetch EAGER のようになります)。しかし、DQL では、fetch EAGER のようには機能せず、追加のクエリが必要になります。可能な解決策は、毎回逆エンティティと結合することです

3) 代替の結果フォーマット (すなわちgetArrayResult()) がいくつかのユースケースに十分である場合、それもこの問題を回避できる可能性があります。

4) 反対側を OneToMany に変更します - 間違っているように見えますが、一時的な回避策になる可能性があります。

5) 部分オブジェクトを強制します。追加のクエリはありませんが、遅延読み込みもありません$query->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true)。たとえば、->select()すべての関連付けで使用することを指定しないと、オブジェクトがいっぱいにならないためエラーが発生する可能性があり、特に選択されていない関連付けはすべて null になります。

6) 逆双方向の OneToOne アソシエーションをマッピングせず、明示的なサービスまたはよりアクティブなレコード アプローチを使用する - https://github.com/doctrine/doctrine2/pull/970#issuecomment-38383961 - そして、Doctrine は閉じられたように見えます問題

于 2015-12-18T10:48:14.427 に答える
12

これはDoctrineで未解決の問題のようです。こちらも参照してください

4.7.1. 1 対 1 の関係を持つエンティティを取得するたびに、余分な SQL クエリが実行されるのはなぜですか?

Doctrine が逆側の 1 対 1 の関連付けを取得していることを検出した場合、このオブジェクトをロードするために追加のクエリを実行する必要があります。これは、そのようなオブジェクトがない (null を設定する) か、プロキシを設定する必要があるかを認識できないためです。このプロキシが持つ ID。現在、この問題を解決するには、この情報を見つけるためにクエリを実行する必要があります。

ソース

于 2013-12-16T13:13:51.770 に答える
2

@apfelboxが説明したように...現在、修正はありません。

一意のキーと組み合わせて OneToMany ソリューションを使用しました。

User.php

/**
 * @ORM\OneToMany(targetEntity="TB\UserBundle\Entity\Settings", fetch="EXTRA_LAZY", mappedBy="user", cascade={"all"})
 */
protected $settings;

/**
 * @return \Doctrine\Common\Collections\Collection
 */
public function getSettings()
{
    return $this->settings;
}

Settings.php

/**
 * @ORM\ManyToOne(targetEntity="TB\UserBundle\Entity\User", fetch="EXTRA_LAZY", inversedBy="settings")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
 */
protected $user;

また、Settings.php の一意性を確保するには、以下を含めます。

use Doctrine\ORM\Mapping\UniqueConstraint;

一意のインデックスを追加します

/**
 * @ORM\Entity
 * @ORM\Table(name="user_settings", uniqueConstraints={@UniqueConstraint(name="user", columns={"user_id"})})
 */
class Settings

したがって、ユーザー設定にアクセスしたいときは、これが必要です(その特定の瞬間にのみ1つのクエリを起動します)

$_settings = $user->getSettings()->current();

私は最もクリーンなソリューションだと思います。

于 2015-10-18T13:39:18.733 に答える
0

別のオプションがあります (これが最適です)。単方向の OneToOne を使用できます。

あなたの場合-UserProfileをめったに使用しない場合-UserProfileにリンクを設定します

/**
 * @var User
 *
 * @ORM\OneToOne(targetEntity="User")
 */
private $user;

そして、それをユーザーにマップしないでください。必要なときにロードできます。

UserProfile を頻繁に使用する場合は、User エンティティの一部にすることができます。

于 2016-02-09T21:00:50.653 に答える
-4

参照によると、オプションの属性を追加できますfetch

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user", fetch="LAZY")
 */
private $userProfile;
于 2012-09-11T08:54:24.183 に答える