我正在尝试使用Common包中的doctrine缓存,但我无法使用一对多,多对一的协议。我稍后会解释我想做什么。
我的配置:
'configuration' => array(
'orm_default' => array(
'metadata_cache' => 'filesystem',
'query_cache' => 'filesystem',
'result_cache' => 'filesystem',
'hydration_cache' => 'filesystem',
)
),
我的实体
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;
}
我的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();
我有 28个类别, 15 他们有parentId。
上面的查询执行 29个SQL查询,但Doctrine只在 1 的缓存中存储,所以当我再次运行此查询时,它会执行 28个查询。
知道我做错了什么吗?缺少一些缓存配置?缺少DQL中的一些方法?我想缓存所有查询,而不仅仅是一个主查询。
修改
我想在循环中使用查询结果,如下所示:
foreach($result as $row)
{
$categories[]['attr'] = $row->getAttribute()->getName();
$categories[]['value'] = $row->getAttribute()->getValue();
}
但这种方式缓存不起作用,所以目前我正在使用:
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();
}
但是我宁愿在数组上处理对象,但是我不能因为我使用对象并且它有关联,那么这个关联不会被缓存。理想情况下,这将是一种缓存对象+所有关联的方法。
答案 0 :(得分:4)
您提供的查询仅提取“父”Category
实体,这些实体通过未初始化的集合为儿童提供水分。当访问该集合时(例如通过迭代这些子集),Doctrine将加载该集合,从而执行另一个查询。它将对第一个查询所包含的所有父类别执行此操作。
将获取模式设置为仅EAGER 会更改这些查询完成的时刻。 Doctrine将在对父类别进行保湿后立即执行它们,它将不会等到您访问集合(如使用获取模式LAZY)。但它仍然会进行这些查询。
告诉Doctrine与孩子一起查询和补充类别的最简单方法是进行“获取连接”查询:
$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();
请注意select()
和leftJoin()
来电。
您也不需要更改关联的获取模式(通过调用setFetchMode()
),因为查询本身会告诉Doctrine执行您想要的操作。
这样做的结果是,如果Doctrine尚未缓存(或缓存是陈旧的),则将执行1个查询;如果缓存(并且仍然是新鲜的),则为0个查询。
属性$parentId
(在Category
中)已重命名为$parent
。此属性将包含父Category
实体或null
,但从不包含ID。
属性$childrenId
已重命名为$children
。此属性将包含Category
个实体的集合(可能为空),但从不是id的集合(或数组),当然也不是单个id。
我建议的查询会考虑这些重命名。
我完全忽略了这样一个事实:在你的“编辑”之后,一个新的Attribute
实体已经出现了。这与你的问题或这个答案无关。恕我直言。
它看起来/听起来像你的类别只使用2个级别(父母和孩子)。当你引入更多关卡(孙子等)时,阅读这个模型会很快变得非常低效。
当进入3个或更多级别时,您可能需要查看Nested Set model。它在写入时更重,但对读取进行了高度优化。
DoctrineExtensions图书馆支持此版本,并且还有一个Symfony Bundle。
答案 1 :(得分: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();
它应该按预期工作。