2014-02-20 28 views
0

我嘗試在多線程環境中創建數據時出現問題。在調用super.create()之前,我檢查數據庫中是否存在數據。但我相信,線程調用同一時間的方法創建(),其中一人投擲失敗,而不是拋出:IllegalArgumentException重複鍵異常:多線程 - 插入過程中出現重複鍵重複例外

@Transactional(readOnly=false) 
    @Override 
    public T create(T resource){ 

     //check if data already exists 
     if(this.repository.findById(resource.getId()) != null){ 
     throw new IllegalArgumentException("Resource already exists"); 
     } 

     //else call create 
     return super.create(resource); 
    } 

有什麼意思,使它的工作原理?該項目使用spring-data-jpa與Hibernate 4.3.1.Final和Oracle數據庫

PS:我需要一個更聰明的解決方案,而不是隻捕獲異常,因爲我在更新過程中遇到同樣的問題,我必須檢查集成日期是否更新資源,如果資源日期比數據庫中存在的日期更新。我的項目不得不在多個節點上運行。

感謝

+0

您使用的是什麼id生成策略? – gadget

+0

嗨,我使用序列生成器策略,但重複鍵不是真的在ID字段,上面的代碼不完全是我使用的代碼。實際上,檢查是在另一個名爲applicationId(findByApplicationId)的字段上,它具有唯一的約束。 –

+0

是來自用戶輸入的其他字段的值?還是生成了?另一個問題與你到目前爲止的答案更相關:你是否在集羣中運行?如果你有多個節點,線程同步將無濟於事。 – gadget

回答

0

你可以使用雙重檢查鎖定(http://en.wikipedia.org/wiki/Double-checked_locking)與同步,以確保將只有一個創建的對象和其他線程簡單地跳過創建硬化的方法。訣竅是檢查對象的存在,然後輸入一個同步塊,然後再檢查一次。

僞代碼:

if (noObjectExists()) { 
    synchronized { 
     if (noObjectExists()) { 
      // Create object 
     } 
    } 
} 
+0

謝謝,但性能呢?我的所有服務都使用這種通用方法(創建) –

+0

完整性與性能;-)嚴重的是,如果創建不是長時間運行的任務,則在第一個線程創建實體之後,後續線程僅檢查存在並立即返回。 – Smutje

0

如果你確信只有一個resourceresource.id您可以resource對象鎖定。

@Transactional(readOnly=false) 
@Override 
public T create(T resource){ 
    synchronized (resource) { 
     //check if data already exists 
     if(this.repository.findById(resource.getId()) != null){ 
      throw new IllegalArgumentException("Resource already exists"); 
     } 

     //else call create 
     return super.create(resource); 
    } 
} 
+0

謝謝,明天我會試試,我認爲這個解決方案,但我怕性能 –

0

插入

  • U可以使用的AtomicInteger獨特的鍵爲

更新用

  • 多線程更新用痛苦的工作之一。您可以使用同步化塊。但這也是性能開銷之一。更新等待不必要的行,即使它與此無關。所以我們可以針對唯一列創建一個鎖。所以同一行不會更新兩次。同樣在Hibernate中,我們可以在同步塊中獲取對象並進行更新。