原则2:在一对多关联中缓存

use*_*508 15 caching symfony doctrine-orm

我正在尝试使用Common包中的doctrine缓存,但我无法使用一对多,多对一的协议.我稍后会解释我想做什么.

我的配置:

'configuration' => array(
    'orm_default' => array(
        'metadata_cache'    => 'filesystem',
        'query_cache'       => 'filesystem',
        'result_cache'      => 'filesystem',
        'hydration_cache'   => 'filesystem',
    )
),
Run Code Online (Sandbox Code Playgroud)

我的实体

class Category
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     *
     * @var string
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=100, nullable=false)
     */
    protected  $name;

    /**
     * @var integer
     *
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="childrenId", fetch="EAGER")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
     */
    protected $parentId;


    /**
     * @ORM\OneToMany(targetEntity="Category", mappedBy="parentId", fetch="EAGER")
     */
    protected $childrenId;
}
Run Code Online (Sandbox Code Playgroud)

我的DQL

$result = $this->em->createQueryBuilder()->select('c')
    ->from('App\Entity\Category', 'c')
    ->where('c.parentId IS NULL')
    ->orderBy('c.priority', 'ASC')
    ->getQuery()
    ->setFetchMode("App\Entity\Category", "parentId", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER);
    ->useResultCache(true, 900, 'categories')
    ->getResult();
Run Code Online (Sandbox Code Playgroud)

我有28个类别,其中15个有parentId.
上面的查询执行29个SQL查询,但Doctrine只在缓存中存储1个,所以当我再次运行此查询时,它会执行28个查询.

知道我做错了什么吗?缺少一些缓存配置?缺少DQL中的一些方法?我想缓存所有查询,而不仅仅是一个主查询.

编辑

我想在循环中使用查询结果,如下所示:

foreach($result as $row)
{
    $categories[]['attr'] = $row->getAttribute()->getName();
    $categories[]['value'] = $row->getAttribute()->getValue();
}
Run Code Online (Sandbox Code Playgroud)

但这种方式缓存不起作用,所以目前我正在使用:

foreach($result as $row)
{
        $attributes = $this->em->createQueryBuilder()->select('c, a.name, a.value')
            ->from('App\Entity\Category', 'c')
            ->innerJoin('App\Entity\Attribute', 'a', 'WITH', 'a.id = c.attribute')
            ->where('c.id = :catId')
            ->setParameter('catId', $row['id'])
            ->getQuery()
            ->useResultCache(true, 900, $categoryName.'-attributes')
            ->getArrayResult();
}
Run Code Online (Sandbox Code Playgroud)

但我宁愿在数组上处理对象,但是我不能因为我使用了对象而且它有关联,那么这个关联就不会被缓存了.理想情况下,这将是一种缓存对象+所有关联的方法.

Jas*_*wer 5

关联获取模式

您提供的查询仅获取“父”实体,这些实体与子实体的未初始化Category集合混合在一起。当访问该集合时(例如,通过迭代这些子集合),Doctrine 将加载该集合,从而执行另一个查询。它将对第一个查询所包含的所有父类别执行此操作。

将 fetch-mode 设置为 EAGER在这些查询完成时发生变化。Doctrine 将在对父类别进行水合后立即执行它们,它不会等到您访问集合(就像使用 fetch-mode LAZY 一样)。但它仍然会执行这些查询。

获取连接查询

告诉 Doctrine 查询类别并与其子类别混合的最简单方法是执行“fetch join”查询:

$queryBuilder = $this->em->createQueryBuilder();
$queryBuilder
    ->select('p', 'c') // [p]arent, [c]hild
    ->from('App\Entity\Category', 'p')
    ->leftJoin('p.children', 'c')
    ->where('p.parent IS NULL')
    ->orderBy('p.priority', 'ASC');

$query = $queryBuilder->getQuery();
$query
    ->useResultCache(true, 900, 'categories')

$result = $query->getResult();
Run Code Online (Sandbox Code Playgroud)

请注意此处的select()leftJoin()调用。

您也不需要更改关联的获取模式(通过调用setFetchMode()),因为查询本身会告诉 Doctrine 执行您想要的操作。

这样做的结果是,如果尚未缓存(或缓存已过时),Doctrine 将执行 1 个查询;如果已缓存(且仍然新鲜),则执行 0 个查询。

假设

属性$parentId( 中Category) 重命名为$parent。此属性将包含父Category实体 或null,但绝不会包含 id。

该属性$childrenId已更名为$children. 该属性将包含实体的集合Category(可能为空),但绝不会包含 id 的集合(或数组),当然也绝不会包含单个 id。

我上面建议的查询考虑了这些重命名。

我完全忽略了这样一个事实:在你的“编辑”之后,一个新的Attribute实体就出现了。恕我直言,它与您的问题或此答案无关。

更多关卡

看起来/听起来你的类别只使用 2 个级别(父级和子级)。当您引入更多级别(孙级等)时,读取此模型可能会很快变得非常低效。

当需要 3 个或更多级别时,您可能需要研究Nested Set 模型。它的写入量较大,但针对读取进行了高度优化。

DoctrineExtensions库对此提供支持,并且还有一个Symfony Bundle


Kam*_*nek 0

根据文档:http://doctrine-orm.readthedocs.io/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql我认为这setFetchMode - EAGER行不通对于您来说,这将产生不会被缓存的额外查询。

为什么不加载带有属性的类别呢?

$result = $this->em->createQueryBuilder()
    ->select('c, a.name, a.value')
    ->from('App\Entity\Category', 'c')
    ->innerJoin('App\Entity\Attribute', 'a', 'WITH', 'a.id = c.attribute')
    ->where('c.parentId IS NULL')
    ->orderBy('c.priority', 'ASC')
    ->getQuery()
    ->useResultCache(true, 900, 'categories')
    ->getResult();
Run Code Online (Sandbox Code Playgroud)

它应该按预期工作。