2015-01-16 56 views
5

我有這個疑問了一段時間,但現在是時候問問它。請參見下面的代碼和$someVar有一個巨大的項目,例如200項目:在foreach循環或外部調用flush()之間的區別,使用哪一個?

// First approach 
foreach($someVar as $item) { 
    $item = $em->getRepository('someEntity')->find($item['value']); 
    $em->remove($item); 
    $em->flush(); 
} 

// Second approach 
foreach($someVar as $item) { 
    $item = $em->getRepository('someEntity')->find($item['value']); 
    $em->remove($item); 
} 

$em->flush(); 
  • 兩個調用會一樣嗎?意味着從數據庫中刪除記錄?
  • 在性能水平上,哪一個最適合使用? (Doctrine有時表現爲記憶殺手)
  • 如果兩種方法都很好,我可以使用相同的每個UPDATE查詢嗎?
  • 如果任何查詢由於某種原因失敗,我如何捕獲哪一個?也可能是錯誤的學說

測試與實際情況

具有設置在回答了幾個疑點留在我心中的良好的信息給出。看看這一段代碼:

foreach ($request->request->get('items') as $item) { 
    $items = $em->getRepository('AppBundle:FacturaProductoSolicitud')->find($item['value']); 

    try { 
     $em->remove($items); 
     $em->flush(); 

     array_push($itemsRemoved, $item['value']); 
     $response['itemsRemoved'] = $itemsRemoved; 
     $response['success'] = true; 
    } catch (\Exception $e) { 
     dump($e->getMessage()); 

     array_push($itemsNonRemoved, $item['value']); 
     $response['itemsNonRemoved'] = $itemsNonRemoved; 
     $response['success'] = false; 
    } 
} 

我使用的是try {} catch() {}句話在這裏,因爲我需要知道哪些$item['value']被刪除或不是爲了將它添加到合適的陣列(見$itemsRemoved$itemsNonRemoved)也爲每個循環執行flush(),我知道不好的做法,但是,從foreach循環中取出flush並在try-catch內部執行,有沒有辦法獲得哪個$item['value']被刪除?怎麼樣?

回答

6

實際上,在每次刪除後運行flush()都是一個反模式。理想情況下,您應該在所有查詢結束時運行一次。在大多數情況下,Doctrine 2已經爲您處理適當的事務劃分:所有的寫操作(INSERT/UPDATE/DELETE)都會被調用,直到EntityManager#flush()被調用,其中包含所有這些更改單筆交易。

但是,如果要獲得更高的一致性,則可以將事務包裝在事務中。實際上,它可以被Doctrine鼓勵,因爲你可以在best practices中看到。

這兩個調用會做同樣的事情嗎?意味着從數據庫中刪除記錄?

是的雖然在實體上調用刪除不會導致在數據庫上發出立即的SQL DELETE。該實體將在下一次涉及該實體的EntityManager#flush()的調用中被刪除。這意味着計劃移除的實體仍然可以查詢並顯示在查詢和收集結果中。

因此,在循環內部刷新意味着大量的SQL查詢和訪問您的數據庫,實體將被立即刪除。

在循環外部沖洗意味着由Doctrine執行的一個高效事務(對數據庫的一次訪問),但實體在調用flush之前不會被實際刪除。這些實體只會被標記爲已刪除。

在性能水平上,哪一個最適合使用?(學說有時表現爲記憶殺手)

毫無疑問,在循環外部沖洗。這也是最好的做法。可能有些情況下,每次你堅持/移除/更新一個實體時,你確實需要執行一次刷新,但是很少。

如果兩種方法都很好,我可以使用相同的每個UPDATE查詢嗎?

同樣適用於更新/保留。不惜一切代價儘量避免沖洗內部循環。請致電documentation。這真的很好解釋。

如果任何查詢由於某種原因失敗,我如何捕獲哪一個?並且可能由原理給出的錯誤

您可以隨時將您的flush包裝在try/catch塊中,並優雅地捕獲查詢失敗時拋出的異常。

try { 
    $em->flush() 
}(\Exception $e) { 
    // do stuff 
    throw $e;// re-thrown Exception 
} 

當使用隱式事務劃分和EntityManager的#沖洗期間發生異常(),事務被自動回滾和EntityManager的關閉。

有關該主題的更多信息here

更新

在你目前的代碼,如果你外循環使用沖洗,去除所有的操作都將屬於同一事務。這意味着如果它們中的任何一個失敗了,就會拋出一個異常併發出回滾(所有被移除的操作都將被回滾,因此不會保留在DB上)。

例如:假設我們有四個項目1,2,3,4,5,6,並假設刪除4失敗。

第一個選項 - >刷新內循環:1,2,3被刪除。 4失敗拋出異常並結束。

第二個選項 - >刷新外循環:4失敗,回滾,沒有被刪除,程序結束。

如果您想要實現的行爲是案例1中顯示的行爲,那麼您可以使用一個選項。但是,在性能方面確實很昂貴。但是,還有更好的解決方案:例如,您可以使用preRemove/postRemove事件的組合來存儲那些在刷新中成功移除的實體的ID(或任何您想要的值)(雖然不會持久化由於回滾)。例如,您可以將它們存儲在屬於該類的靜態數組中(或使用單例或其他)。然後,在異常的catch子句中,可以使用該數組迭代並對這些項執行刪除操作(當然,在循環外部刷新)。

然後,您可以返回數組,以便讓用戶知道您實際上刪除了這些實體並且因爲刪除過程中存在問題而爲false。

+0

優秀的答案,但仍有一些疑問,你可以看看我的主要帖子的編輯? – ReynierPM

+0

@ReynierPM謝謝:)我已經更新了答案,回答你的問題,希望它可以幫助 – acontell

+0

又一個驚人的答案,但是請你在離開我的解釋上顯示一些代碼?我不是Symfony的專家,對我來說有些晦澀難懂,但我還在學習,對不對? – ReynierPM

相關問題