2016-10-06 174 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