Java 锁定数据库行的简单方法

Java 锁定数据库行的简单方法,java,database,locking,record,Java,Database,Locking,Record,我遇到了一些遗留代码,它们应该在序列表中保留递增的序列号。此序列号将用作另一个表(订单表)中新记录的ID 我认为它应该做的是: 如果有此序列的记录,则获取该值并返回该数字+1 如果没有记录,则扫描实际表格以找到当前最大值,将其四舍五入到最接近的1000,并将该最大值记录在序列表格中 代码如下: private static final long SEQUENCE_BLOCK_SIZE = 1000; private static final String ID_FIELD_NAME = "Ord

我遇到了一些遗留代码,它们应该在
序列表中保留递增的序列号。此序列号将用作另一个表(订单
表)中新记录的ID

我认为它应该做的是:

  • 如果有此序列的记录,则获取该值并返回该数字+1
  • 如果没有记录,则扫描实际表格以找到当前最大值,将其四舍五入到最接近的1000,并将该最大值记录在
    序列
    表格中 代码如下:

    private static final long SEQUENCE_BLOCK_SIZE = 1000;
    private static final String ID_FIELD_NAME = "Order_ID";
    private static final String TABLE_NAME = "Orders";
    private static long lastID = 0;
    String init = null;
    
    public long newID() throws Exception {
      Connection c = null;
      long id = 0;
    
      try {
        c = Connections.getConnection(init);
        id = nextID(c);
      } catch(Exception e) {
        try {
          c.close();
        } catch(Exception ignore) {
        }
        throw e;
      } finally {
        if ( c != null ) {
          Connections.putConnection(c);
        }
      }
    
      return id;
    }
    
    /**
     * Returns a new unique id for the account.
     */
    protected static synchronized long nextID(Connection c) throws Exception {
      // Only update the table occasionally.
      if(lastID % SEQUENCE_BLOCK_SIZE == 0) {
        Statement s = null;
        ResultSet r = null;
    
        try {
          lastID = 0;
    
          s = c.createStatement();
    
          // Lock the row. +++ EH??? +++
          s.executeUpdate("UPDATE sequences SET sequence_value=sequence_value WHERE sequence_name='" + ID_FIELD_NAME + "'");
    
          // Get the current value.
          r = s.executeQuery("SELECT sequence_value FROM sequences WHERE sequence_name='" + ID_FIELD_NAME + "'");
          if(r.next()) {
            lastID = r.getLong(1);
          }
          r.close();
    
          s.close();
    
          if(lastID == 0) {
            // Get the current max value from the table.
            s = c.createStatement();
            r = s.executeQuery("SELECT MAX(" + ID_FIELD_NAME + ") FROM " + TABLE_NAME + "");
            if(r.next()) {
              lastID = ((r.getLong(1) + SEQUENCE_BLOCK_SIZE) / SEQUENCE_BLOCK_SIZE) * SEQUENCE_BLOCK_SIZE;
            }
            r.close();
            s.close();
    
            // Insert the new row.
            s = c.createStatement();
            s.executeUpdate("INSERT INTO sequences(sequence_value,sequence_name) VALUES(" + (lastID + SEQUENCE_BLOCK_SIZE) + ",'" + ID_FIELD_NAME + "')");
            s.close();
          }else {
            // Update the row.
            s = c.createStatement();
            s.executeUpdate("UPDATE sequences SET sequence_value=" + (lastID + SEQUENCE_BLOCK_SIZE) + " WHERE sequence_name='" + ID_FIELD_NAME + "'");
            s.close();
          }
        } catch(Exception e) {
          throw e;
        } finally {
          try {
            r.close();
          } catch(Exception e) {
          }
          try {
            s.close();
          } catch(Exception e) {
          }
        }
      }
    
      return lastID++;
    }
    
    我的问题是,当
    序列
    表中没有记录时,它不会添加新记录,尽管它正在执行
    插入
    。我已经分别测试了
    插入
    ,它似乎工作正常。我相信这与
    //锁定行
    语句有关。我找不到任何文档表明该语句确实会锁定该行,甚至不会产生什么效果

    我正在针对SQLServer2008进行测试,但同样的机制应该适用于2000+和Oracle

    针对评论添加了

    我同意,对于唯一的序列号,使用本机数据库机制会更好/更有效。不幸的是,这个应用程序被设计用来驱动六个不同的数据库系统中的任何一个,当然这两个系统都可以,所以坚持使用这种技术会更好


    我们在自动提交模式下运行会话。为什么插入的
    没有创建新记录?这与试图锁定有关吗?

    为什么不使用选择…进行更新*struct

    问题是我没有参加交易。我在
    autocommit
    模式下运行会话是错误的


    记录锁的作用正如@Sérgio在其评论中所说。

    在Oracle中,update语句将锁定记录以更新其他会话,但所有尝试查询该值的会话都将检索更新前的信息。请删除代码并使用本机序列器。数据库提供它们是有原因的。你能给我一个参考吗?我可以对Oracle和SQL使用相同的代码吗?@SérgioMichels锁能保持多久/什么会导致锁被释放?@Paul我没有书面参考。这是我在数据库类中记得的。锁定将一直保持,直到您在该会话上使用提交。同样,如果其他会话选择最大值,它将返回旧值,直到会话提交更改。我同意Ineradial的观点,最好使用本地序列。你真的想以此作为我问题的答案吗?