树根主义

时间:2014-12-15 07:20:21

标签: php database symfony orm doctrine-orm

我想在数据库中获取树的根,它可以正常工作,但问题在于它为500个节点查询数据库500次,有没有办法改进这个?

$query = $this->createQueryBuilder('n')
            ->where('n.parent IS NULL')
            ->getQuery();`

实体结构:

Radio\ArchiveBundle\Entity\Category:
    type: entity
    table: null
    repositoryClass: Radio\ArchiveBundle\Repository\CategoryRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        name:
            type: string
            length: 255
        isActive:
            type: boolean
            nullable: true

    oneToMany:
        tracks:
            targetEntity: Radio\ArchiveBundle\Entity\Track
            mappedBy: category
    manyToOne:
        parent:
            targetEntity: Radio\ArchiveBundle\Entity\Category
            inversedBy: childrens
            fetch: EAGER
    oneToMany:
        childrens:
            targetEntity: Radio\ArchiveBundle\Entity\Category
            mappedBy: parent
            cascade: [persist, remove]

Symfony2探查器图片: enter image description here

2 个答案:

答案 0 :(得分:1)

为什么要查询500个?

正如您所说,您已从数据库中获取了500个根节点。在请求中的某处,访问属性Category::$childrens。可能在视图中调用Category上使用该属性的方法(如$node->getChildren())。

访问该属性意味着Doctrine需要加载Collection才能获取该节点的子节点。这导致对每个根节点的查询。因此,您还有500个额外的查询。

如果您需要访问子节点,在查询根节点时立即获取它们是明智的:

SELECT rt, chld FROM Category rt LEFT JOIN rt.childrens chld WHERE rt.parent IS NULL

这样,根节点的直接子节点与根节点本身一起被水合。您将查询数量从501减少到只有1。

PS:复数形式的“孩子”是“孩子”(不是“孩子”)。

嵌套集模型

恕我直言,可以通过切换到Nested Set model来改进类别树的结构。虽然在向树中插入新节点时性能稍差,但更新和选择树变得更加便宜。

例如:使用您现在使用的父/子模型获取整个树,每个级别的深度需要大约1个查询(或者每个级别需要1个JOIN,但这仅在已知最大深度时才有效)。使用嵌套集模型,您只需一个查询即可获取整个树(无论深度如何)。当深度增加时,这将大大提高性能。

查看Tree behavior库的Doctrine Extensions,您可以使用它轻松实现嵌套集模型。还有一个Symfony 2 bundle来集成该库。

只需将其添加到Composer并按照the instructions

composer.phar require stof/doctrine-extensions-bundle

答案 1 :(得分:0)

您的父实体被热切地获取并且自己有一个引用。

manyToOne:
    parent:
        targetEntity: Radio\ArchiveBundle\Entity\Category
        inversedBy: childrens
        fetch: EAGER

因为它是字段parentchildren的自引用。 在获取类别时,它也会获取children(如您所料)。但是也会获取这些连接实体的父实体(当它们存在时)。导致额外的查询。这种情况一直持续到所有父母都被遍历为止。