2012-06-01 28 views
3

我正在查看EntityManager API,並試圖瞭解我將執行記錄鎖定的順序。基本上,當用戶決定要編輯的記錄,我的代碼是:實體類和記錄鎖定

entityManager.getTransaction().begin(); 
r = entityManager.find(Route.class, r.getPrimaryKey()); 
r.setRoute(txtRoute.getText()); 
entityManager.persist(r); 
entityManager.getTransaction().commit(); 

從我的試驗和錯誤,看來我需要.begin()後設置WWEntityManager.entityManager.lock(r, LockModeType.PESSIMISTIC_READ);

我很自然地認爲我會在提交之後使用WWEntityManager.entityManager.lock(r, LockModeType.NONE);,但它給了我這樣的:

Exception Description: No transaction is currently active 

我還沒有試過把它提交尚未過,但不會是打敗的目的鎖定記錄,因爲我的目標是避免碰撞記錄,以防50個用戶試圖一次提交更改?

任何幫助,如何在編輯期間鎖定記錄,非常感謝!

謝謝!

回答

1

在事務內部執行鎖定非常有意義。鎖在事務結束時自動釋放(提交/回滾)。在事務之外鎖定(在JPA的情況下)是沒有意義的,因爲釋放鎖與事務結束有關。另外,在執行更改和提交事務後進行鎖定並沒有太大意義。

它可以是您正在使用悲觀鎖定比他們真正用於其他目的。如果我的假設是錯誤的,那麼你可以忽略答案的結束。當您的交易對實體(行)持有悲觀讀鎖時,以下情況有保證:

  • 沒有髒讀:其他事務無法看到您對鎖定行執行的操作的結果。
  • 可重複讀取:不對其他交易進行修改
  • 如果您的交易修改鎖定的實體,那麼PESSIMISTIC_READ升級到PESSIMISTIC_WRITE,或者如果鎖定無法升級,交易將失敗。

繼粗略描述的情況與獲得在事務開始鎖定:

entityManager.getTransaction().begin(); 
r = entityManager.find(Route.class, r.getPrimaryKey(), 
     LockModeType.PESSIMISTIC_READ); 
//from this moment on we can safely read r again expect no changes 
r.setRoute(txtRoute.getText()); 
entityManager.persist(r); 
//When changes are flushed to database, provider must convert lock to 
//PESSIMISTIC_WRITE, which can fail if concurrent update 
entityManager.getTransaction().commit(); 

通常數據庫沒有對悲觀讀單獨的支持,所以你實際上是拿着鎖,因爲PESSIMISTIC_READ來排。只有在沒有對鎖定行進行更改時,也可以使用PESSIMISTIC_READ。如果上述更改始終完成,那麼從一開始就使用PESSIMISTIC_WRITE是合理的,因爲它可以幫助您避免併發更新的風險。

在許多情況下,使用樂觀而非悲觀鎖定也是有意義的。 Locking and Concurrency in Java Persistence 2.0

+0

完美的解釋,並有很大的聯繫!非常足智多謀,謝謝! –

0

偉大的工作試圖在寫鎖不斷變化的數據安全:良好範例和鎖定策略之間進行選擇的一些意見可以被發現。 :)但是你可能會過度/做很長的路。

  • 首先小點。不需要調用persist()。爲了更新,只需修改從find()返回的實體的屬性。entityManager自動知道更改並在提交期間將它們寫入數據庫。堅持當你創建一個新的對象&寫入到數據庫首次(或添加一個新的子對象的父關係和級聯通過級聯堅持= PERSIST)時才需要。

  • 大多數應用程序有「衝突」併發更新由有自己獨立的交易和獨立的持久上下文不同的線程相同的數據的可能性很低。如果這是真的爲你和你想最大限度地提高可擴展性,然後用一種樂觀的寫鎖,而不是一個悲觀的讀取或寫入鎖。絕大多數Web應用程序都是這種情況。它給完全相同的數據的完整性,更好的性能/可擴展性,但是你必須(很少)處理的OptimisticLockException。

  • 通過簡單地在db和entity中擁有一個short/integer/long/TimeStamp屬性並通過@Version在實體中註釋它,樂觀寫鎖定是自動內置的,您不需要調用entityManager.lock( )在這種情況下

如果你滿足上述的,你增加了一個@Version屬性的實體,您的代碼將是:

try { 
    entityManager.getTransaction().begin(); 
    r = entityManager.find(Route.class, r.getPrimaryKey()); 
    r.setRoute(txtRoute.getText()); 
    entityManager.getTransaction().commit(); 
} catch (OptimisticLockException e) { 
    // Logging and (maybe) some error handling here. 
    // In your case you are lucky - you could simply rerun the whole method. 
    // Although often automatic recovery is difficult and possibly dangerous/undesirable 
    // in which case we need to report the error back to the user for manual recovery 
} 

即沒有明確鎖定在所有人 - 實體管理器自動處理它。

假如你有強烈的需求,以避免併發數據更新「衝突」,並高興地與有限的可擴展你的代碼,然後連載通過悲觀寫鎖的數據訪問:

try { 
    entityManager.getTransaction().begin(); 
    r = entityManager.find(Route.class, r.getPrimaryKey(), LockModeType.PESSIMISTIC_WRITE); 
    r.setRoute(txtRoute.getText()); 
    entityManager.getTransaction().commit(); 
} catch (PessimisticLockException e) { 
    // log & rethrow 
} 

在這兩種情況下,成功提交或具有自動回滾的異常意味着所有執行的鎖定都會自動清除。

乾杯。