跳到主要內容

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據文件,Doctrine 有 Pessimistic Lock,又分為兩種:
  1. LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE
  2. LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE
差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取,且可以繼續 Select LOCK IN SHARE MODE。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取,但不能 Select LOCK IN SHARE MODE。MySQL 文件有更詳細的比較與情境使用的說明,參考網址

現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock,但 find 是透過 id 來處理。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。

因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下:
 19     public function depositAction()
 20     {
 21 
 22         $em = $this->getDoctrine()->getManager();
 23             
 24         $em->transactional(function ($em) {
 25             $entityName = 'AcmeTrainingBundle:Account';
 26             $lockMode   = LockMode::PESSIMISTIC_READ;
 27             $orderBy    = null;
 28             $criteria   = array('user' => 'chuck');
 29             
 30             // 版本一:find one entities
 31             // 參考自:EntityRepository::findOneBy()
 32             $persister  = $em->getUnitOfWork()->getEntityPersister($entityName);
 33             $this->account = $persister->load($criteria, null, null, array(), $lockMode, 1, $orderBy);
 34             
 35             // 版本二:find many entities
 36             // 改寫自:BasicEntityPersister::loadAll()
 37             // 修改原始碼:BasicEntiyPersister
 38             // public $_rsm
 39             // public function _getSelectEntityiesSQL()
 40             // public function _selectJoinSql()
 41             // public function expandParameters()
 42             $limit = 5; 
 43             $persister  = $em->getUnitOfWork()->getEntityPersister($entityName); 
 44             $sql = $persister->_getSelectEntitiesSQL($criteria, null, $lockMode, $limit, null, $orderBy);
 45             
 46             $sql = str_replace('OFFSET 0', '', $sql);   // $sql 會產生 offset = 0 造成語法錯誤,故手動取代掉
 47             
 48             list($params, $types) = $persister->expandParameters($criteria);
 49             $stmt = $em->getConnection()->executeQuery($sql, $params, $types);
 50             
 51             $hydrator = $em->newHydrator(($persister->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
 52             
 53             $this->accounts = $hydrator->hydrateAll($stmt, $persister->_rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
 54         });
 55 
 56         return new Response('Version 1:
Account id:' . $this->account->getId()
 57             . '

Version 2:
Number of Accounts: ' . count($this->accounts));
 58     }

版本一是鎖定單一筆資料的方式,較為簡單且易懂。
版本二是鎖定多筆資料的方式,方法並不完美,必須修改原始碼,故不建議使用。

簡言之,如果要鎖定單一筆資料,可採用上述方式。但若要鎖定多筆資料,我會建議放棄用 ORM,直接自己下 SQL 比較快且簡潔,而資料的操作改為一般方式(非 ORM)。

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...