2010-06-07 41 views
8

我正在更改我的JPA代碼以利用線程。我有一個單獨的實體管理器和每個線程的事務。JPA原子查詢/保存爲多線程應用程序

我用什麼有(對於單線程環境)是這樣的代碼:

// get object from the entity manager 
X x = getObjectX(jpaQuery); 

if(x == null) 
{ 
    x = new X(); 
    x.setVariable(foo); 
    entityManager.persist(x); 
} 

隨着我越來越重複鍵,因爲,我認爲多線程環境中的代碼,getObjectX一個返回null線程,然後該線程被換出,下一個線程調用getObjextX,同時變爲null,然後這兩個線程將創建並保留一個新的X()。

短同步增加的,是有一個原子的方式來獲得/保存-IF-doesn't-存在的價值與JPA或者我應該重新考慮我的做法

編輯:

我使用最新的EclipseLink和MySQL 5.1

編輯2:

我添加同步... MASSIVE性能下降(到如此地步,也不能使用)。將所有數據收集回主線程,然後在該線程上進行創建。

回答

4

簡短的悲傷答案是沒有JPA API不能爲你做。整個API或多或少地建立在樂觀的原則上,假設事情正在發揮作用並且在併發修改的情況下拋出異常。

如果這種情況經常發生,那麼可能有其他組件(不管是否生成foo?)可能會受益於進行線程安全,也許是查詢+創建同步的替代方案。

+0

我不得不做相反的事情,創造Foo的東西超出了我的控制範圍,它幾乎保證有重複。我將不得不收集線程中的所有數據,然後將它們全部存儲在一個線程中。 – TofuBeer 2010-06-07 20:24:32

2

我認爲您需要在「jpaQuery」中使用的字段上添加一個唯一約束,以便數據庫不能使用該查詢的約束中使用的相同條件創建重複行。調用代碼將需要捕獲由於違反約束而產生的異常(理想情況下它將是一個EntityExistsException,但規範在這種情況下不明確)。

+0

我確實有獨特的約束,我得到了異常。我希望找到一種方法來做到這一點,而不會引發異常... – TofuBeer 2010-06-07 20:00:15

+1

使用異常可能是這種情況下最簡單的解決方案,它類似於也在失敗時使用異常的樂觀鎖定。 – 2010-06-12 13:06:09

0

您確定您需要多個entitymanagers嗎?在類似的情況,我只是用一個EntityManager的和簡單的每個方法鎖定對象:

private Object customerLock = new Object[0]; 

public Customer createCustomer(){ 
    Customer customer = new Customer(); 
    synchronized(customerLock){ 
     entityManager.persist(customer); 
    } 
    return customer; 
} 

編輯:OK,不能做很多有關性能只是說,它在我的應用程序進行確定,但爲唯一性使用這樣的事情:

public Customer getOrCreateCustomer(String firstName, String lastName){ 
    synchronized(customerLock){ 
     List<Customer> customers = 
      entityManager.createQuery(
       "select c from Customer c where c.firstName = :firstName" 
       + " and c.lastName = :lastName" 
      ) 
      .setParam("firstName", firstName) 
      .setParam("lastName", lastName) 
      .setMaxResults(1) 
      .getResultList(); 
     if(customers.isEmpty()){ 
      Customer customer = new Customer(firstName, lastName); 
      entityManager.persist(customer); 
     }else{ 
      customer = customers.get(0); 
     } 
    } 
    return customer; 
} 
+0

我需要確保您的客戶的對象不存在。我嘗試添加同步,但性能差遠了。 – TofuBeer 2010-06-07 20:22:51

+0

Java級別的同步在羣集中不起作用 – Vadzim 2015-02-19 18:02:07

3

一些 「黑客」 來考慮:

(obj.getClass().getName() + String.valueOf(hashCode())).intern() 

  • 基於對象的業務鍵(而不是生成的ID)
  • 同步上實現hashCode()equals()

    因此,您只會在相關案例中獲得鎖定。

+0

嗯......我已經有了這樣的等號/哈希碼......從來沒有想過要對它們進行同步。我會試一試看看。 – TofuBeer 2010-06-07 22:58:11

+0

這很聰明:-) – 2010-06-08 04:45:11

+0

這不會在羣集中工作 – Vadzim 2015-02-19 18:00:16