Sequences
テーブルに増分するシーケンス番号を保持すると思われるいくつかのレガシーコードが出てきました。このシーケンス番号は、別のテーブル(Orders
テーブル)の新しいレコードのIDとして使用されます。私はそれをやっていることになっていると思う何データベースの行をロックする簡単な方法
は次のとおりです。
- この一連のレコードがある場合は、値を取得し、レコードがない場合は、その番号+ 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に対しても動作するはずです。
コメントに応じて。
固有のシーケンス番号に対してネイティブデータベースメカニズムを使用するほうが効率的です。悲しいことに、このアプリは約6種類のデータベースシステムのいずれかを駆動するように設計されており、確かにOracleとMS SQLの両方になっています。
セッションを自動コミットモードで実行します。 INSERT
が新しいレコードを作成しないのはなぜですか?それはロックの試みと関係がありますか?
Oracleでは、更新ステートメントは他のセッションの更新のためにレコードをロックしますが、値を照会しようとするすべてのセッションは、更新前にそこにあった情報を取得します。 –
コードを取り除き、ネイティブシーケンサーを使用してください。データベースがそれらを提供する理由があります。 – millimoose
@Inerdial私に参考にしてもらえますか? OracleとSQLの両方に同じコードを使用できますか? – OldCurmudgeon