2016-10-06 149 views
1

我的问题可能是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存储库的查询将找不到第一个对象,并且会在数据库中创建重复项。

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

回答

3

这是一个记忆工作!

// 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

很好的事情要知道,谢谢! – Hammerbot

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(); 
+0

感谢您的回复。其实,我已经在分工。真实情况如下。我需要导入用户的旧数据库。我决定做200个用户的几笔交易。当我导入每个用户的多个地址时,问题就出现了。所以每个用户只有一个或两个地址,我不想每次刷新那些地址。 – Hammerbot

+1

Hi @timdev,经过一天的思考,我想我终于明白你的意思了。事实上,我并没有按照你向我展示的方式使用交易,我认为我也会使用它,并结合记忆。所以,非常感谢你的信息,这个话题让我学到了两件重要的事情! – Hammerbot

+0

@El_Matella我很高兴你明白了。我不确定,但昨天没有时间进入。一旦你开始手动管理交易,你会发现越来越多的地方有利于这样做。 – timdev