簡體   English   中英

JDBC使用SELECT FOR UPDATE鎖定一行,不起作用

[英]JDBC Lock a row using SELECT FOR UPDATE, doesn't work

我遇到MySQL的SELECT問題.. FOR UPDATE,這是我試圖運行的查詢:

SELECT * FROM tableName WHERE HostName='UnknownHost' 
        ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE

在此之后,相關的線程將執行UPDATE並更改HostName,然后它應該解鎖該行。

我正在運行一個多線程java應用程序,所以3個線程正在運行這個SQL語句,但是當線程1運行它時,它不會從線程2和3鎖定其結果。因此線程2和3得到相同的結果,他們可以更新同一行。

每個線程也都有自己的mysql連接。

我正在使用Innodb,事務隔離= READ-COMMITTED,並且在執行select for update之前自動提交已關閉

我可能會錯過什么嗎? 或許還有更好的解決方案? 非常感謝。

代碼:

public BasicJDBCDemo()
{
    Le_Thread newThread1=new Le_Thread();
    Le_Thread newThread2=new Le_Thread();
    newThread1.start();
    newThread2.start();         
}

線程:

class Le_Thread extends Thread  
{

    public void run() 
    {
    tring name = Thread.currentThread().getName();
        System.out.println( name+": Debut.");
    long oid=Util.doSelectLockTest(name);
    Util.doUpdateTest(oid,name);        
    }

}

選擇 :

public  static long doSelectLockTest(String threadName)
  {
    System.out.println("[OUTPUT FROM SELECT Lock ]...threadName="+threadName);
    PreparedStatement pst = null;
    ResultSet rs=null;
    Connection conn=null;
    long oid=0;
    try
    {
     String query = "SELECT * FROM table WHERE Host=? 
                               ORDER BY Timestamp asc limit 1 FOR UPDATE";


      conn=getNewConnection();
      pst = conn.prepareStatement(query);
      pst.setString(1, DbProperties.UnknownHost);
      System.out.println("pst="+threadName+"__"+pst);
      rs = pst.executeQuery();

      if (rs.first())
      {
        String s = rs.getString("HostName");
        oid = rs.getLong("OID");
        System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName);

      }   

    }
    catch (SQLException ex)
    {
      ex.printStackTrace();
    }
    finally
    {
        DBUtil.close(pst);
        DBUtil.close(rs);
        DBUtil.close(conn);
    }
    return oid;
  }

請幫忙.... :

結果:

Thread-1: Debut.
Thread-2: Debut.
[OUTPUT FROM SELECT Lock ]...threadName=Thread-1
New connection..
[OUTPUT FROM SELECT Lock ]...threadName=Thread-2
New connection..
pst=Thread-2: SELECT * FROM b2biCheckPoint  WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE
pst=Thread-1: SELECT * FROM b2biCheckPoint  WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE
oid_oldest/host/threadName==1/UnknownHost/Thread-2
oid_oldest/host/threadName==1/UnknownHost/Thread-1
[Performing UPDATE] ... oid = 1, thread=Thread-2
New connection..
[Performing UPDATE] ... oid = 1, thread=Thread-1
pst_threadname=Thread-2: UPDATE b2bicheckpoint SET HostName='1_host_Thread-2',UpdateTimestamp=1294940161838 where OID = 1
New connection..
pst_threadname=Thread-1: UPDATE b2bicheckpoint SET HostName='1_host_Thread-1',UpdateTimestamp=1294940161853 where OID = 1

你非常困惑,但至少在編輯后事情看起來更好。 有多種方法可以做到這一點,但我發現最好的方法是實際使用JDBC的ResultSet.update*方法:

首先,您需要使用ResultSet.CONCUR_UPDATABLE參數准備SELECT ... FOR UPDATE語句,如下所示:

ps = conn.prepareStatement(query,
                           ResultSet.TYPE_FORWARD_ONLY,
                           ResultSet.CONCUR_UPDATABLE);

然后,您必須使用ResultSet實際更新表:

if(rs.next())
{
    rs.updateString(columnIndex, "new_hostname");
    rs.updateRow();
}

第三,您可能需要使用我可以在更新中看到的事務。 希望您的DbUtil.close方法不會拋出任何異常,檢查null等。另外,如果您的方法變得更復雜,那么您也應該有回滾邏輯。

你不應該因為任何原因修改my.ini

您創建的用於選擇更新的連接需要與用於執行更新的連接相同。 否則它不是同一個事務的一部分,它會釋放鎖,所以你的其他線程也會開始執行它。 所以在你的代碼中你需要這樣做:

if (rs.first())
  {
    String s = rs.getString("HostName");
    oid = rs.getLong("OID");
    System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName);

  }   
Util.doUpdateTest(oid,name,conn);
conn.commit();

Select語句不能導致工作結果集由導致排序的操作引起的任何組或順序引起。 選擇必須在桌面上保持活動位置,任何排序都不會提供。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM