Doctrineで1対1のjoinをした時の注意点

Doctrineで1対1のjoinをした場合、不必要なSQLが発行される場合があります。

要は、Lazy Loadが起こってしまうということです。
1対多で起こるLazy Loadについては、以下の記事で書いています。
PHPのDoctrineでSQLが乱発される事象について

以下は、userとaddressが1対1のリレーションを持っているEntityです。
※この記事ではDoctrine 2.4.7を使用しています。

class User implements UserInterface
{
    /**
    * @var integer $id
    *
    * @ORM\Column(name="id", type="integer")
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    private $id;

    /**
    * @var string $name
    *
    * @ORM\Column(name="name", type="string", length=255)
    */
    private $name;

    /**
     * @ORM\OneToOne(targetEntity="Address", inversedBy="user")
     * @ORM\JoinColumn(name="address_id", referencedColumnName="id", nullable=true)
     * @var Address
     */
    private $address;

    /**
    *
    * @return integer
    */
    public function getId()
    {
        return $this->id;
    }

    /**
    * @param string $name
    */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
    * @return string
    */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return Address
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * @param Address $address
     */
    public function setAddress($address)
    {
        $this->address = $address;
    }
}
class Address
{
    /**
    * @var integer $id
    *
    * @ORM\Column(name="id", type="integer")
    * @ORM\Id
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    private $id;

    /**
    * @var string $name
    *
    * @ORM\Column(name="name", type="string", length=255)
    */
    private $name;

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

    /**
    *
    * @return integer
    */
    public function getId()
    {
        return $this->id;
    }

    /**
    * @param string $name
    */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
    * @return string
    */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return User
     */
    public function getUser()
    {
        return $this->user;
    }

    /**
     * @param User $user
     */
    public function setUser($user)
    {
        $this->user = $user;

    }
}

ここで、ユーザーを全て取得するために

$this->get('doctrine.orm.entity_manager')->getRepository('User')->findAll();

とした場合、DoctrineはuserテーブルにaddressテーブルをLEFT JOINしたSQLを自動的に発行します。

以上のことから、Doctrineは、「Entityに定義されているOneToOneのJOINを自動的にLEFT JOINする」ということがわかります。

例えば、User Entityにaddressの他に2つOneToOneのJOINが定義されていたとしたら、
全ユーザーのデータが欲しいだけだったとしても、自動的に3つのテーブルがLEFT JOINされてしまいます。

これを防ぐためには、以下のDQLが必要です。

$this->createQueryBuilder('user')->getQuery()->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)->getResult();

こうすることによって、余計なSQLが発行されずに全ユーザーのデータを取得することができます。

Entityに複数のOneToOneのJOINが定義されている場合は必要に応じて

setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)

を忘れないようにしましよう!


コメントを残す

メールアドレスが公開されることはありません。

Time limit is exhausted. Please reload CAPTCHA.