Я натолкнулся на какой-то устаревший код, который должен хранить порядковый номер в таблице Sequences
. Этот порядковый номер будет использоваться в качестве идентификатора для новых записей в другой таблице (таблица Orders
).
Я думаю, что это должно быть сделано:
- Если для этой последовательности есть запись, получите значение и верните это число + 1.
- Если записи нет, отсканируйте фактическую таблицу, чтобы найти текущий максимум, округлите его до ближайшей 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
не создает новую запись? Это как-то связано с попыткой блокировки?