锁定数据库行的简单方法

时间:2012-01-27 17:22:32

标签: java database locking record

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

我认为它应该做的是:

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

    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++;
    }
    

    我的问题是,当Sequences表中没有记录时,它没有添加新记录,尽管它正在执行INSERT。我已经分别测试了INSERT,它似乎运行正常。我认为这与//Lock the row声明有关。我找不到任何暗示该语句确实会锁定该行甚至会产生什么影响的文档。

    我正在测试SQL Server 2008,但这种机制应该适用于2000+和Oracle。

    已添加以回应评论。

    我接受将本机数据库机制用于唯一序列号会更好/更有效。遗憾的是,这个应用程序旨在驱动大约六个不同的数据库系统中的任何一个,当然OracleMS SQL,所以最好坚持这种技术。

    我们以自动提交模式运行会话。为什么INSERT没有创建新记录?是否与锁定尝试有关?

2 个答案:

答案 0 :(得分:0)

为什么不使用 SELECT ... FOR UPDATE * 结构?

答案 1 :(得分:0)

问题在于我没有提交交易。关于以autocommit模式运行会话我错了。

记录锁定就像@Sérgio在评论中所说的那样。