java - JDBC 使用 SELECT FOR UPDATE 锁定一行,不起作用

标签 java sql jdbc

我在使用 MySQL 的 SELECT .. FOR UPDATE 时遇到问题,这是我尝试运行的查询:

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

在此之后,相关线程将执行更新并更改主机名,然后它应该解锁该行。

我正在运行一个多线程 Java 应用程序,因此有 3 个线程正在运行此 SQL 语句,但是当线程 1 运行此语句时,它不会锁定线程 2 和 3 的结果。因此线程 2 和 3 正在获取相同的结果,他们可以更新同一行。

而且每个线程都在自己的 mysql 连接上。

我正在使用 Innodb,事务隔离 = READ-COMMITTED,并且在执行更新选择之前关闭自动提交

我可以错过什么吗?或者也许有更好的解决方案? 非常感谢。

代码:

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

关于java - JDBC 使用 SELECT FOR UPDATE 锁定一行,不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4682871/

相关文章:

java - JMenuBar 下降到 "custom"JPanel 和 "erased"

Java Hibernate 数据异常 : cannot be converted to BIGINT

mysql - 带有内部连接和子查询的 SQL

mysql - 如果在另一个表中找到记录,则从表中选择

java - 戏剧框架中 Actor 的正确使用(java)

java - 在端口 11111 上监听 * 地址

java - AndroMDA maven 代码生成和 JPA 注释

SQL 和转义的 XML 数据

postgresql - 使用 pg_dump 和 psql -U postgres db_name < ... 移动数据库导致 "ERROR: relation "table_name“不存在”

java - sql BIT 转 Java