学说在补水过程中增加了额外的查询,造成 "正常 "的一对一和自称关系的n+1问题

问题描述 投票:0回答:1

News 彼此之间的关系采用一对多的自称方式(一个新闻是家长,可以有很多孩子)。更重要的是,每个 News 有正常的(非自称)一对一的关系,与 EventGallery . 当我运行简单的DQL。

SELECT n FROM App\Entity\News n WHERE n.parent = :id

然后将结果通过 getResults 方法,默认 HYDRATION_OBJECT 值集,在 getResults 方法。

SELECT t0.* FROM event t0 WHERE t0.news_id = 2 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM gallery t0 WHERE t0.news_id = 2 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM event t0 WHERE t0.news_id = 1 AND ((t0.deleted_at IS NULL));
SELECT t0.* FROM gallery t0 WHERE t0.news_id = 1 AND ((t0.deleted_at IS NULL));

其中 news_id = 1news_id = 2 是第一个查询选择的新闻的子代。

News 也有不自引用的一对多关系(我在这里忽略了它们),但水合没有对它们进行额外的查询。问题只发生在有 parent 所涉关系 where 声明。

如何复制

// news Entity
/**
 * @ORM\Entity(repositoryClass="App\Repository\NewsRepository")
 * @ORM\Table(uniqueConstraints={@UniqueConstraint(name="news_slug_deleted", columns={"slug","deleted_at"})})
 */
class News {
    use SoftDeleteableEntity;

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $title;

    /**
     * @Gedmo\Slug(fields={"title"})
     * @ORM\Column(length=128)
     */
    private $slug;
/**
     * @ORM\OneToOne(targetEntity="App\Entity\Gallery", mappedBy="news", cascade={"persist", "remove"}, orphanRemoval=true)
     *
     * @var Gallery
     */
    private $gallery;

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\Event", mappedBy="news", cascade={"persist", "remove"}, orphanRemoval=true)
     *
     * @var Event
     */
    private $event;
    /**
     * One News has Many News.
     * @ORM\OneToMany(targetEntity="News", mappedBy="parent")
     */
    private $children;

    /**
     * Many News have One News.
     * @ORM\ManyToOne(targetEntity="News", inversedBy="children")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
     */
    private $parent;
}
/**
 * @ORM\Entity(repositoryClass="App\Repository\EventRepository")
 * @Gedmo\SoftDeleteable()
 */
class Event
{
    use SoftDeleteableEntity;

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;
    /**
     * @ORM\OneToOne(targetEntity="App\Entity\News", inversedBy="event", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $news;

Gallery 实体类似于 Event 所以我在这里忽略了它。

// News controller

public function index(NewsRepository $newsRepository, $slug)
{
        $news = $newsRepository->findOneBy(['slug' => $slug]);
        $newsRepository->getConnectedNews($news->getId());
}
// news repository

class NewsRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, News::class);
    }
    public function getConnectedNews($newsId) {
        $query = $this->createQueryBuilder('n')->andWhere('n.parent = :id')->setParameter('id', $newsId);
        return $query->getQuery()->getResult(AbstractQuery::HYDRATE_OBJECT);
    }
}

补水的 news 其中有20个孩子,最后会有: 20*2+1(n*r+1)个查询,其中:

  • (n) 20是孩子的数量
  • (r)2是一比一关系的数目。
  • 1是基本查询

我想阻止Doctrine对一对一关系的额外查询。这是Doctrine的bug还是不需要的行为,还是我犯了什么错误?

总而言之,我想只获取所有自引用的子代,而不需要一对一的关系,因为我没有要求检索它们,所以它应该只用一个查询来获取所有子代。news 而不需要对每个检索到的 "新闻 "对象和每个它的一对一关系进行额外的查询。

php symfony doctrine-orm doctrine select-n-plus-1
1个回答
1
投票

在该实体中你有一对反向的OneToOne关系。

逆OneToOne关系不能被Doctrine懒加载,很容易成为性能问题。

如果你 真的 需要将这些关系映射到 方面(而不仅仅是在拥有方)确保明确地进行适当的连接,或者将这些联系标记为 FETCH=EAGER 这样Doctrine就可以为你进行连接。

例如,一个可以避免可怕的 "n+1 "问题的查询是。

SELECT n, g, e
    FROM App\Entity\News n
    LEFT JOIN n.gallery g
    LEFT JOIN n.event e
WHERE n.parent = :id
© www.soinside.com 2019 - 2024. All rights reserved.