检查Doctrine中是否存在持久化和非持久化实体

时间:2016-10-06 16:04:58

标签: symfony doctrine-orm

我的问题可能是this one的重复,但我找不到任何令人满意的答案,所以我会尝试使这个更精确。

我正在从其他API构建导入服务。我不想在我的新数据库中有任何重复。

所以这是我当前实现的一个例子:

控制器:

public function mainAction () 
{
    $em = $this->getDoctrine()->getManager();

    $persons_data = [
        [
            'first_name' => 'John',
            'last_name' => 'Doe'
        ],
        [
            'first_name' => 'John',
            'last_name' => 'Doe'
        ]
    ];

    $array = [];

    foreach($persons_data as $person_data) 
    {
        $person = $this->get('my_service')->findOrCreatePerson($person_data);
        $array[] = $person;
    }

    $em->flush();

    return new Response();
}

服务功能:

public function findOrCreatePerson ($data) 
{
    $em = $this->em;

    $person = $em->getRepository('AppBundle:Person')->findOneBy([
        'first_name' => $data['first_name'],
        'last_name' => $data['last_name']
    ]);

    if(is_null($person)) {
        $person = new Person();
        $person->setFirstName($data['first_name']);
        $person->setLastName($data['last_name']);
        $em->persist($person);
    }

    return $person
}

我尽量让它尽可能简单。

正如您所看到的,我想只进行一次数据库事务以获得一些性能改进。

问题是,如果我在findOrCreatePerson()方法的末尾不刷新,则对Person存储库的查询将找不到第一个对象,并将在数据库中创建重复项。

我的问题很简单:我该如何实施这样的事情?

2 个答案:

答案 0 :(得分:3)

这是memoize的工作!

// Cache
private $persons = [];

public function findOrCreatePerson ($data) 
{
    // Need unique identifier for persons
    $personKey = $data['first_name'] . $data['last_name'];

    // Already processed ?
    if (isset($this->persons[$personKey])) {
        return $this->persons[$personKey];
    }
    $em = $this->em;

    $person = $em->getRepository('AppBundle:Person')->findOneBy([
        'first_name' => $data['first_name'],
        'last_name' => $data['last_name']
    ]);

    if(is_null($person)) {
        $person = new Person();
        $person->setFirstName($data['first_name']);
        $person->setLastName($data['last_name']);
        $em->persist($person);
    }

    // Cache
    $this->persons[$personKey] = $person;

    return $person
}

答案 1 :(得分:2)

Cerad的回答(备忘录)很好,但我鼓励你重新考虑一些事情。

  

正如您所看到的,我只想进行一次数据库事务以获得一些性能改进。

这句话有些不对劲。

主要的一点是,您将flush()与单个原子事务进行混淆。您可以手动管理交易边界,这样做通常非常有利。

第二件事是,当你谈论批​​量导入时,你会很快发现你遇到的第一个性能问题根本就不是数据库。它是EntityManager本身。随着EM的内部身份地图变得膨胀,持续存在于DB的计算变化变得非常非常缓慢。

我考虑按如下方式重写你的核心循环,看看它是否足够快。只有在必要时才考虑备忘。

$em->beginTransaction();
foreach($persons_data as $person_data) 
{
    $person = $this->get('my_service')->findOrCreatePerson($person_data);
    $em->flush();
    $em->clear();  // don't keep previously inserted entities in the EM.
}
$em->commit();