Symfony4 – 两个实体管理器共享缓存池,是个坏主意还是遇到了bug?

这将是一个很长的帖子,我遇到了一个奇怪的行为,我在profiler中看到一个实体管理器被说成是在映射它没有映射的实体。它看起来像这样。bottom of doctrine section in profiler这里是doctrine. yaml:

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   "pdo_mysql"
                host:     "127.0.0.1"
                port:     "3306"
                dbname:   "example"
                user:     "root"
                password: ""
                charset:  utf8mb4
                server_version: "mariadb-10.4.10"
            logs:
                driver:   "pdo_mysql"
                host:     "127.0.0.1"
                port:     "3306"
                dbname:   "example_logs"
                user:     "root"
                password: ""
                charset:  utf8mb4
                server_version: "mariadb-10.4.10"
    orm:
        auto_generate_proxy_classes: true
        default_entity_manager: default
        entity_managers:
            default:
                query_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                metadata_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                result_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                connection: default
                naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
                mappings:
                    App:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/Main'
                        prefix: 'App\Entity\Main'
                        alias: App
            logs:
                query_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                metadata_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                result_cache_driver:
                    type: pool
                    pool: apcu.default.cache.pool
                connection: logs
                naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
                mappings:
                    LogBundle:
                        is_bundle: false
                        type: annotation
                        dir: '%kernel.project_dir%/src/Entity/Logs'
                        prefix: 'App\Entity\Logs'
                        alias: App

这里是framework.yaml,有缓存池的配置。

framework:
    secret: '%env(APP_SECRET)%'
    session:
        handler_id: null
        cookie_secure: auto
        cookie_samesite: lax

    php_errors:
        log: true

    cache:
        pools:
            apcu.default.cache.pool:
                adapter: cache.adapter.apcu
            apcu.logs.cache.pool:
                adapter: cache.adapter.apcu

如果我删除 metadata_cache_driver 日志配置 entity_manager 配置,或者将其改为使用不同于默认实体管理器的缓存池(apcu.logs.cache.pool),那么profiler就会报告正确的映射(例如在默认em和logs em中的实体是空的)。

这个问题只发生在实体是feed trough形式和 $form->handleRequest() 处理,创建或修改没有表单的实体不会引起这样的问题。这是我的控制器。

<?php

namespace App\Controller;

use App\Entity\Main\Example;
use App\Form\Type\ExampleType;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends AbstractController {
    /**
     * @Route("/example1")
     * @Template
     */
    public function example1(EntityManagerInterface $em){
        $example = new Example();
        $example->setValue('example value');

        try {
            $em->persist($example);
            $em->flush();
        } catch(\Exception $e){
            return new Response('An error has occurred. '.$e->getMessage());
        }

        return [];
    }

    /**
     * @Route("/example2")
     * @Template
     */
    public function example2(EntityManagerInterface $em){
        $example = $em->getRepository(Example::class)->find(1);
        if(!$example){
            return new Response('No example found.');
        }

        $example->setValue(mt_rand(0, mt_getrandmax()));
        try {
            $em->flush();
        } catch(\Exception $e){
            return new Response('An error has occurred. '.$e->getMessage());
        }

        return [];
    }

    /**
     * @Route("/example3")
     * @Template
     */
    public function example3(Request $request, EntityManagerInterface $em){
        $example = $em->getRepository(Example::class)->find(1);
        if(!$example){
            return new Response('No example found.');
        }

        $form = $this->createForm(ExampleType::class, $example);
        $form->handleRequest($request);

        if($form->isSubmitted() && $form->isValid()){
            $em->flush();
        }

        return ['form' => $form->createView()];
    }


}

example1和example2路由 导致问题的原因,只有example3是这样的,而且只有在提交表单的时候,所以只有当我输入example3的url,然后点击提交表单的时候,只有当进入这个请求的profiler的时候,我才能看到这个问题。

我的最小重现例子是创建新的symfony LTS项目。symfony new example-site --version=lts --full

那么这些都是我后来改过的文件。

file changes

数据库是由 symfony console doctrine:database:create --connection=defaultsymfony console doctrine:database:create --connection=logs 然后通过以下方式创建表格 symfony console doctrine:migrations:diff --em=defaultsymfony console doctrine:migrations:migrate --em=default

这里是其他文件的代码,我还没有把它放在帖子里。

<?php
//src/Entity/Main/Example.php
namespace App\Entity\Main;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Example {
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    private $value;

    public function getId(){
        return $this->id;
    }

    public function getValue(){
        return $this->value;
    }

    public function setValue(string $value){
        $this->value = $value;
    }
}
<?php
//src/Form/Type/ExampleType.php
namespace App\Form\Type;

use App\Entity\Main\Example;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ExampleType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options){
        $builder->add('value', TextType::class);
        $builder->add('submit', SubmitType::class);
    }

    public function configureOptions(OptionsResolver $resolver){
        $resolver->setDefaults([
            'data_class' => Example::class,
        ]);
    }
}
<!-- template/s/example/example1.html.twig -->
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Example</title>
</head>
<body>
    Example1
</body>
</html>
<!-- template/s/example/example2.html.twig -->
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Example</title>
</head>
<body>
    Example2
</body>
</html>
<!-- template/s/example/example3.html.twig -->
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Example</title>
</head>
<body>
{{ form(form) }}
</body>
</html>

最后我想补充的是,在其他项目中,这个问题更加明显,因为当实体对其他实体有引用时,就会报告错误(在非拥有方的一对多自引用关联中)。other example with an error reported在这种情况下,Item 实体是一个feed trough form.对于那些好奇的人来说,这里是Item.php:但我不知道这有什么关系,因为它不是由日志实体管理器管理的,不应该出现在默认实体管理器下。

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ItemRepository")
 * @ORM\Table(indexes={
 *          @ORM\Index(name="item_image", columns={"image"})
 *     })
 */
class Item {

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=32)
     * @Assert\NotBlank()
     * @Assert\Length(min=3, max=32)
     */
    private $name;

    /**
     * @ORM\Column(type="string")
     */
    private $description = '';

    /**
     * @ORM\Column(type="string", length=25, nullable=true)
     */
    private $image;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Item", mappedBy="container")
     */
    private $items;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Item", inversedBy="items")
     * @ORM\JoinColumn(name="container", referencedColumnName="id")
     * @var $container Item
     */
    private $container;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\TagItem", mappedBy="item")
     * @var $tags TagItem[]
     */
    private $tags;

    /**
     * @Assert\Image(mimeTypes="image/jpeg")
     * @var $imageFile null|UploadedFile
     */
    private $imageFile;

    public function __construct() {
        $this->items = new \Doctrine\Common\Collections\ArrayCollection();
        $this->tags = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getId(){
        return $this->id;
    }

    public function getName(){
        return $this->name;
    }

    public function setName(string $name){
        $this->name = $name;
    }

    public function getDescription(){
        return $this->description;
    }

    public function setDescription($description){
        $this->description = $description;
    }

    public function hasImage(){
        return isset($this->image);
    }

    public function getImage(){
        return $this->image;
    }

    public function setImage($image){
        $this->image = $image;
    }

    public function hasImageFile(){
        return isset($this->imageFile);
    }

    public function getImageFile(){
        return $this->imageFile;
    }

    public function setImageFile($imageFile){
        $this->imageFile = $imageFile;
    }

    public function getItems(){
        return $this->items;
    }

    public function hasContainer(){
        return isset($this->container);
    }

    public function getContainer(){
        return $this->container;
    }

    public function setContainer(?Item $container){
        return $this->container = $container;
    }

    public function getTags(){
        return $this->tags;
    }

    public function setTags($tags){
        $this->tags = $tags;
    }
}

PHP版本是7.3.12,托管的是 symfony serve

解决方案:

我回到github上,在 问题线索花式网络 谁说的

状态:已审查

这个错误与WebProfilerBundle无关。原因是你对2个实体管理器使用了相同的缓存盐。在某些地方,我们盲目地依次在两个EM上调用getClassMetadata()(即:DoctrineLoader,DoctrineExtractor)。第一次对该EM的调用会填充缓存。第二次调用在不应该知道该类的EM上打入缓存,从而认为该类已被加载。

使用相同的缓存是可以的,只是需要使用不同的盐。

当我问到在配置中是否有为实体管理器设置盐的选项时,我得到的回答是 凳子:

简单的实现方式是使用2个独立的缓存池,因为FrameworkBundle在共享同一存储时,会将池名考虑到种子中,以隔离每个池中的密钥。

给TA打赏
共{{data.count}}人
人已打赏
未分类

使用函数LastQuarter+2查找上学期的情况

2022-9-9 6:25:16

未分类

separator else add a new entry

2022-9-9 6:25:18

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索