2012-03-01 44 views
1

我很感謝幫助理解爲什麼我的應用程序無法找到一個實體,即使它存在於數據庫中;我相信這個問題涉及併發的寫作/閱讀。我正在使用jpa2 /休眠4和彈簧3.並行寫作和閱讀JPA2 /休眠

我有一個方法,創建一個用戶,然後將ID作爲JSON對象消息發送到用戶進一步處理的消息隊列。當消息處理程序(UserProcessor.class)嘗試使用find用戶時(見下文),會發生問題。

Registration.class

@Transactional 
public Response createUser(String firstName, String lastName) { 
    User tmpUser = new User(firstName, lastName); 
    User savedUser = this.em.merge(tmpUser); 

    this.em.flush(); 

    if (savedUser != null) { 
    processUser(savedUser.getId()); // message sent to queue. 
    } else { 
    // Throw exception... 
    } 
} 

UserProcessor.class

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class}) 
public void processUser(Long id) { 
    User user = this.em.find(User.class, id); // No user entity is found, "user" is null. 
    if (user == null) { 
    // throw exception 
    } 
    ... 
} 
+0

爲什麼不寫你的方法直接傳遞用戶作爲參數:public void processUser(final User aUser)'? – 2012-03-01 20:50:38

+0

而您面臨的問題可能與事務相關。 – 2012-03-01 20:51:42

+0

@nico_ekito:因爲消息系統被java和非java服務使用,所以我選擇使用json數據結構而不是將消息系統綁定到java。 – Ari 2012-03-01 21:09:39

回答

0

您可以使用兩種方法:

@Transactional(rollbackFor={javax.ws.rs.WebApplicationException.class}) 
public void processUser(Long id) { 
    processUser(this.em.find(User.class, id)); 
} 

public void processUser(User aUser) { 
... 
} 
1

我認爲你可能有一個併發問題。

據我理解你的代碼,它的工作原理是這樣的:

Registration.createUser:

  • 步驟A1),打開交易(I)
  • 步驟A2)創建用戶和步驟A4)將用戶標識放入隊列
  • 步驟A4)提交交易(I)

    UserProcessor.processUser(長ID)

  • 步驟B1)開從隊列

  • 步驟B2)的用戶ID打開一個事務(II)
  • 步驟B3)由加載用戶其交易中的ID(II)
  • 步驟B4)做的東西
  • 步驟B5)提交事務(II)

您知道(取決於您的事務隔離級別),只有在提交第一個事務(I)時,寫入事務(I)的數據纔可以在其他事務(II)中讀取。

因此,如果UserProcessor.processUser在步驟A4中提交事務(I)之前嘗試處理步驟B3,它將​​不會在數據庫中看到用戶。 (如果使用更高的事務隔離級別,則可能需要在B2之前執行步驟A4。)

一種解決方法是切換步驟A3和A4的順序。重要的一點:如果在其他(外部)事務的上下文中調用方法Response.createUser,那麼它將與外部事務一起提交!