繁体   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