2013-06-27 193 views
1

由於遺留代碼問題,我需要手動計算唯一索引,並且在將新行插入數據庫時​​不能使用auto_increment。交易開始時的鎖定表

問題是多個客戶端(不同機器)的多個插入可以同時發生。因此,噹噹前事務處於活動狀態時,我需要鎖定具有最高ID的行被其他事務讀取。或者,我可以鎖定整個表從任何讀取。在這種情況下,時間不是問題,因爲寫入/讀取非常少見(< 1 op /秒)

它試圖將隔離級別設置爲8(Serializable),但是隨後MySQL會引發DeadLockException。有趣的是,確定下一個ID的SELECT仍然完成,這與我對可序列化的理解相矛盾。

同時將LockMode設置爲PESSIMISTIC_READ的select,似乎沒有幫助。

public void insert(T entity) { 
    EntityManager em = factory.createEntityManager(); 
    try { 
     EntityTransaction transaction = em.getTransaction(); 
     try { 
      transaction.begin(); 

      int id = 0; 
      TypedQuery<MasterDataComplete> query = em.createQuery(
        "SELECT m FROM MasterDataComplete m ORDER BY m.id DESC", MasterDataComplete.class); 
      query.setMaxResults(1); 
      query.setLockMode(LockModeType.PESSIMISTIC_READ); 
      List<MasterDataComplete> results = query.getResultList(); 
      if (!results.isEmpty()) { 
       MasterDataComplete singleResult = results.get(0); 

       id = singleResult.getId() + 1; 
      } 

      entity.setId(id); 

      em.persist(entity); 
      transaction.commit(); 
     } finally { 
      if (transaction.isActive()) { 
       transaction.rollback(); 
      } 
     } 
    } finally { 
     em.close(); 
    } 
} 

有些話到應用程序:
這是Java的獨立,運行在其連接到同一個數據庫服務器的多個客戶端,它應該與多個DB服務器上運行(任何地方的Sybase,Oracle,MySQL等.. )

目前我剩下的唯一想法就是做插入操作,並捕獲ID已經在使用時發生的異常,然後重試。這可以工作,因爲我可以假定該列設置爲主鍵/唯一。

+0

你有選擇通過它實現一個服務並暴露數據庫嗎? –

+0

不可悲。整個架構是一片混亂(最多15年),所有客戶端都直接連接到數據庫。 當我現在要實現它時,我會選擇一個連接到數據庫和瘦客戶端的中央服務器,它只需要從服務器請求並顯示數據。但不幸的是,客戶有超過20萬的LOC,並且完全重寫它不是一種選擇。 :( – ssindelar

+0

每次生成一個唯一標識符怎麼樣?由基於時間的部分加上隨機部分組成,或者加密安全的唯一標識符。沒有事務隔離或樂觀鎖定問題,並且易於實現。 – Grzegorz

回答

0

的問題是,與PESSIMISTIC_READ你阻止別人UPDATE該行最高的ID。如果你想阻止其他的選擇你需要使用PESSIMISTIC_WRITE

我知道這似乎很奇怪,因爲你不會更新該行.. ..但如果你想要其他塊,而執行一個SELECT時,你應該鹼灰和說:「乾草都.. ..我讀這個行,並將更新它「..所以他們將不被允許讀取該行數據庫引擎認爲你會在提交之前修改它。

SERIALIZABLE本身根據文檔將所有普通SELECT語句轉換爲SELECT ...鎖定在共享模式中,所以不會超過您明確已經在做的事情。