2012-04-04 42 views
2

我的Java應用程序需要以獨立方式執行一組原始數據庫語句。例如,應用程序需要從一個表中讀取數據行並更新另一個表中的數據行。以Java鎖定一組數據庫操作

QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner 

Object[] params = new Object[] { param }; 

Connection conn = null; 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 
    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} catch (SQLException e) { 
    // 
} finally { 
    DBUtils.closeQuietly(conn); 
} 

事務管理通過將連接的自動提交設置爲false並稍後顯式提交來實現,如上所示。但上面的代碼也可以在多線程環境中執行,我也希望兩個DB語句(選擇&更新)作爲一個整體相互獨立運行。

我有一些想法在該方法中使用共享Java Lock對象,如下所示。

在課堂上,

private Lock lock = new ReentrantLock(); // member variable 

在該方法中,

lock.lock(); 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 

    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} finally { 
    DBUtils.closeQuietly(conn); 
    lock.unlock(); 
} 

看來排序解決問題的能力。但是,我想知道這是否是最佳做法,並且有沒有更好的選擇(如框架)?

回答

1

我的建議是讓數據庫爲你管理這些鎖而不是你的應用程序。這處理了有多個JVM運行代碼的情況。您提到的鎖定機制只能在單個JVM中有效。

完成此操作的方法是執行SELECT ... FOR UPDATE。這將對選定的行進行鎖定,並且當您的事務被提交或回滾時鎖定將被釋放。這比表級鎖更好,因爲這些行仍然可以被其他只讀取當前值但不更新它們的事務讀取。如果另一個交易試圖獲得一個FOR UPDATE鎖,那麼它將阻塞,直到第一個鎖完成。

+0

謝謝,Hiro2k。我相信,我們的應用程序運行在一個servlet容器中,並將在單個JVM中運行。 「SELECT ... FOR UPDATE」可能不適用於我的情況,因爲我正在更新select語句操作的表以外的表中的一行。 – Chen 2012-04-04 17:36:40

+0

沒錯,但是如果你決定添加服務器的另一個實例來進行負載平衡或縮放,那麼你會遇到這個問題。您可以在方法一開始的第二個表格的行中添加「SELECT ... FOR UPDATE」,以便其他人不得不等待它完成。 – Hiro2k 2012-04-04 18:05:05

+0

好點,@ Hiro2k。我會評估我們的使用案例,看看是否可以涵蓋所有這些案例。 – Chen 2012-04-04 18:09:58

1

要實現atomicy所需的唯一方法是使用數據庫中的存儲過程來隔離數據並將其全部鎖定。在Java級別的鎖定無法輕鬆完成數據庫中的鎖定。

+0

我的建議只有在相關數據通過對單個JVM中同一對象的方法調用進行更新時纔有效。它本身不會鎖定數據庫級別。這就是說,它不會阻止其他意外更新在其他方法觸發的同一段數據上(如其他方法調用,其他應用程序)。這可以通過利用存儲過程來減輕嗎? – Chen 2012-04-04 17:51:34

1

您可以處理類似問題的另一種方法是對所有數據庫事務使用可序列化的事務隔離級別。這會導致任何一組事務的行爲就好像它們一次只運行一次,而沒有實際使它們一次運行一個事務。如果你這樣做,你應該使用一個捕獲序列化失敗的框架(SQLState 40001)並重試事務。最重要的是,您不必擔心交易之間的特定交互 - 如果交易在唯一運行的情況下做出正確的事情,它將在任何交易組合中做正確的事情。

請注意,所有的事務都必須是可序列化的,因此這個工作非常簡單。

0

從我的理解,你只是想讓包含選擇和更新語句代碼塊爲線程安全?這就是使用同步關鍵字的原因。即使這個問題被問及很久,我只想在這裏記下它。把這些代碼行放在synchronized塊中。