Symfony4.1 Doctrine ManyToMany 减少查询次数
Symfony4.1 Doctrine ManyToMany Reduce No of Queries
我正在做一个项目。实体是博客、类别、标签。博客和标签处于 ManyToMany 关系中。我通过标签过滤器获取数据的存储库查询是。
代码 1:
/**
* @return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
// ->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
这段代码工作正常。所有标签(post 中的标签数量)都正确显示。但是DB Query的No是14。然后当我取消注释 select 时,
代码 2:
/**
* @return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
查询次数为9。但每个Post的标签只有一个(不显示单个post的所有标签)。
要明确信息:
- 显示整个博客列表Post。
- 但不是 Post 的所有标签。
- 每个 Post 只显示一个标签。
问题: code1 是否正确(数据库查询数 = 14)或者我是否需要稍微调整一下以减少 no数据库命中率。请指导我。
这是两种情况下的预期行为。
案例 1) 您只是 select BlogPost 实体。因此,您告诉 doctrine 获取所有具有 slug = value 的 BlogTag 的 BlogPost。
SQL 查询仅生成 returns 来自 blog_post table 的列值,因此 仅滋润返回的 BlogPost 实体 ,它不会为每个 BlogPost 中的 BlogTags 集合添加水分。
当您尝试访问 BlogPost 的标签时 会生成一个新查询 以获取并补充其集合。
这就是在这种情况下您得到 更多查询 的原因。
案例 2) 您 select 也过滤了 BlogTag 实体,并且学说 hydrates(puts) 只有这个过滤的 BlogTag每个 BlogPost 的集合。
当您尝试访问 BlogPost 的 BlogTag 时,您会得到符合查询生成器中条件的筛选标签。
要强制 doctrine "reload" 来自数据库的数据,您应该刷新 blogPost 实体:
$em->refresh($blogPost);
并且还在关系定义的 cascade operations 上包含 refrech 选项:
@OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})
参考文献:
- what cascade refresh means in doctrine 2
- refresh objects: different question but same solution
感谢@Jannes Botis 更新。但就我而言,代码本身是错误的。需要稍作改动。
BlogTags.php
/**
* @ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
*/
private $blogPosts;
BlogPost.php
/**
* @var Collection|BlogTags[]
*
* @ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
* @ORM\JoinTable(
* name="fz__blog_n_tag",
* joinColumns={
* @ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
* @ORM\OrderBy({"name": "ASC"})
*/
private $blogTags;
这创建了 join_table。 好了,我有一个 join_table。虽然此代码仅供参考
Controller.php
// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
Repository.php
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('t')
->select('t','p','tx')
->innerJoin('t.blogPosts', 'p')
->innerJoin('p.blogTags', 'tx')
->where('p.isActive = :val1')
->andWhere('t.slug = :val2')
->setParameter('val1', true)
->setParameter('val2', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
我没有完全改变我的旧树枝文件。因为它在很多地方抛出错误。因为现在我正在使用标签回购而不是博客。所以我用
修改了树枝
{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}
帮我解决这个问题(twig 文件): 只有一个标签,这就是 |first twig 过滤器的原因
用这个树枝过滤器澄清我。我做得对吗。给我建议改进它。我试过 bp[0] 这个 trows error.
最后: 通过在控制器中使用旧代码,它 returns 14 db 命中。现在 returns 只有 8 个。甚至 post 中还有更多标签(但旧的 returns 更多)。
我正在做一个项目。实体是博客、类别、标签。博客和标签处于 ManyToMany 关系中。我通过标签过滤器获取数据的存储库查询是。
代码 1:
/**
* @return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
// ->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
这段代码工作正常。所有标签(post 中的标签数量)都正确显示。但是DB Query的No是14。然后当我取消注释 select 时,
代码 2:
/**
* @return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
查询次数为9。但每个Post的标签只有一个(不显示单个post的所有标签)。
要明确信息:
- 显示整个博客列表Post。
- 但不是 Post 的所有标签。
- 每个 Post 只显示一个标签。
问题: code1 是否正确(数据库查询数 = 14)或者我是否需要稍微调整一下以减少 no数据库命中率。请指导我。
这是两种情况下的预期行为。
案例 1) 您只是 select BlogPost 实体。因此,您告诉 doctrine 获取所有具有 slug = value 的 BlogTag 的 BlogPost。 SQL 查询仅生成 returns 来自 blog_post table 的列值,因此 仅滋润返回的 BlogPost 实体 ,它不会为每个 BlogPost 中的 BlogTags 集合添加水分。
当您尝试访问 BlogPost 的标签时 会生成一个新查询 以获取并补充其集合。
这就是在这种情况下您得到 更多查询 的原因。
案例 2) 您 select 也过滤了 BlogTag 实体,并且学说 hydrates(puts) 只有这个过滤的 BlogTag每个 BlogPost 的集合。
当您尝试访问 BlogPost 的 BlogTag 时,您会得到符合查询生成器中条件的筛选标签。
要强制 doctrine "reload" 来自数据库的数据,您应该刷新 blogPost 实体:
$em->refresh($blogPost);
并且还在关系定义的 cascade operations 上包含 refrech 选项:
@OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})
参考文献:
- what cascade refresh means in doctrine 2
- refresh objects: different question but same solution
感谢@Jannes Botis 更新。但就我而言,代码本身是错误的。需要稍作改动。
BlogTags.php
/**
* @ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
*/
private $blogPosts;
BlogPost.php
/**
* @var Collection|BlogTags[]
*
* @ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
* @ORM\JoinTable(
* name="fz__blog_n_tag",
* joinColumns={
* @ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
* @ORM\OrderBy({"name": "ASC"})
*/
private $blogTags;
这创建了 join_table。 好了,我有一个 join_table。虽然此代码仅供参考
Controller.php
// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
Repository.php
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('t')
->select('t','p','tx')
->innerJoin('t.blogPosts', 'p')
->innerJoin('p.blogTags', 'tx')
->where('p.isActive = :val1')
->andWhere('t.slug = :val2')
->setParameter('val1', true)
->setParameter('val2', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
我没有完全改变我的旧树枝文件。因为它在很多地方抛出错误。因为现在我正在使用标签回购而不是博客。所以我用
修改了树枝{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}
帮我解决这个问题(twig 文件): 只有一个标签,这就是 |first twig 过滤器的原因 用这个树枝过滤器澄清我。我做得对吗。给我建议改进它。我试过 bp[0] 这个 trows error.
最后: 通过在控制器中使用旧代码,它 returns 14 db 命中。现在 returns 只有 8 个。甚至 post 中还有更多标签(但旧的 returns 更多)。