2

我有一個從數據庫讀取的Spring事務,如果找不到它,它會創建它。例如:如何讓兩個完全相同的讀寫併發事務等待對方?

@Transactional 
public int getUserIdCreateIfNotExistent(String email) throws Exception { 
    try { 
    return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email); 
    } catch (IncorrectResultSizeDataAccessException e) { 
    Thread.sleep(10000); 
    return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email); 
    } 
} 

當這個方法被調用兩次的同時,將在具有兩個不同的用戶在數據庫相同的電子郵件的結果(有獨特的電子郵件沒有限制,這只是爲了演示)。

我希望第二個事務等待第一個事務,以便只創建一個用戶。我試着將事務隔離級別更改爲Isolation.SERIALIZABLE,但它所做的只是使提交最後一次回滾的事務。

編輯: 之前有人建議使用​​或類似的東西鎖定的Java解決方案,是要明白,這個方法是一個Web應用程序的一部分,在特定端點被擊中稱爲是非常重要的。該Web應用程序在多個不同的計算機上運行,​​並且負載平衡,並且在端點被同時調用兩次時發生問題。這意味着代碼可能在兩臺不同的計算機上並行執行,與另一臺計算機上的同一個數據庫對話

+0

隔離級別不會幫助,因爲你插入新行。除非遇到獨特的限制。 –

回答

0

解決與諮詢鎖:

@Transactional 
public int getUserIdCreateIfNotExistent(String email) throws Exception { 
    try { 
    jdbcTemplate.queryForRowSet("SELECT pg_advisory_xact_lock(hashtext('users'), hashtext(?))", email); 
    return jdbcTemplate.queryForObject("SELECT id FROM users WHERE email = ?", Integer.class, email); 
    } catch (IncorrectResultSizeDataAccessException e) { 
    Thread.sleep(10000); 
    return jdbcTemplate.queryForObject("INSERT INTO users (email) values(?) RETURNING id", Integer.class, email); 
    } 
} 
0

您可以使用「鎖定」表。它將包含您需要鎖定的所有內容,即表名(例如「用戶」)和行密鑰(例如「電子郵件」)。

如果您需要更快的速度,那麼您可以在整個服務器上使用memcached進行復制。如果你的插入不是很多,那麼數據庫解決方案可能會很好。

相關問題