2012-01-27 7 views
0

Sequencesテーブルに増分するシーケンス番号を保持すると思われるいくつかのレガシーコードが出てきました。このシーケンス番号は、別のテーブル(Ordersテーブル)の新しいレコードのIDとして使用されます。私はそれをやっていることになっていると思う何データベースの行をロックする簡単な方法

は次のとおりです。

  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に対しても動作するはずです。

コメントに応じて。

固有のシーケンス番号に対してネイティブデータベースメカニズムを使用するほうが効率的です。悲しいことに、このアプリは約6種類のデータベースシステムのいずれかを駆動するように設計されており、確かにOracleMS SQLの両方になっています。

セッションを自動コミットモードで実行します。 INSERTが新しいレコードを作成しないのはなぜですか?それはロックの試みと関係がありますか?

+1

Oracleでは、更新ステートメントは他のセッションの更新のためにレコードをロックしますが、値を照会しようとするすべてのセッションは、更新前にそこにあった情報を取得します。 –

+1

コードを取り除き、ネイティブシーケンサーを使用してください。データベースがそれらを提供する理由があります。 – millimoose

+0

@Inerdial私に参考にしてもらえますか? OracleとSQLの両方に同じコードを使用できますか? – OldCurmudgeon

答えて

0

問題は、私がトランザクションをコミットしていないことでした。私はautocommitモードで私たちのセッションを実行することについて間違っていた。

レコードロックは、@Sérgioが彼のコメントで述べたように機能します。

0

なぜ使用していませんSELECT ... FOR UPDATE * struct?

+0

本当に私の質問への答えとしてそれを意味しますか? OldCurmudgeon

関連する問題