Простой способ заблокировать строку базы данных - PullRequest
0 голосов
/ 27 января 2012

Я натолкнулся на какой-то устаревший код, который должен хранить порядковый номер в таблице Sequences. Этот порядковый номер будет использоваться в качестве идентификатора для новых записей в другой таблице (таблица Orders).

Я думаю, что это должно быть сделано:

  1. Если для этой последовательности есть запись, получите значение и верните это число + 1.
  2. Если записи нет, отсканируйте фактическую таблицу, чтобы найти текущий максимум, округлите его до ближайшей 1000 и запишите этот максимум в таблицу Sequences.

Вот код:

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.

Добавлено В ответ на комментарии.

Я принимаю, что было бы лучше / эффективнее использовать собственный механизм базы данных для уникальных порядковых номеров. К сожалению, это приложение предназначено для работы с любой из шести различных систем баз данных и, конечно, с Oracle и MS SQL , поэтому придерживаться этого метода было бы предпочтительнее.

Мы запускаем наши сеансы в режиме автоматической фиксации. Почему INSERT не создает новую запись? Это как-то связано с попыткой блокировки?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2012

Проблема заключалась в том, что я не совершал транзакцию. Я ошибся из-за запуска наших сеансов в режиме autocommit.

Блокировка записи действует так, как сказал @ Сержио в своем комментарии.

0 голосов
/ 28 января 2012

Почему вы не использовали SELECT ... FOR UPDATE * struct?

...