由於遺留代碼問題,我需要手動計算唯一索引,並且在將新行插入數據庫時不能使用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已經在使用時發生的異常,然後重試。這可以工作,因爲我可以假定該列設置爲主鍵/唯一。
你有選擇通過它實現一個服務並暴露數據庫嗎? –
不可悲。整個架構是一片混亂(最多15年),所有客戶端都直接連接到數據庫。 當我現在要實現它時,我會選擇一個連接到數據庫和瘦客戶端的中央服務器,它只需要從服務器請求並顯示數據。但不幸的是,客戶有超過20萬的LOC,並且完全重寫它不是一種選擇。 :( – ssindelar
每次生成一個唯一標識符怎麼樣?由基於時間的部分加上隨機部分組成,或者加密安全的唯一標識符。沒有事務隔離或樂觀鎖定問題,並且易於實現。 – Grzegorz