1

次の列を持つ CUSTOMERS という名前のテーブルがあります。

CUSTOMER_ID (NUMBER)、DAY(DATE)、REGISTERED_TO(NUMBER)

テーブルにはさらに列がありますが、上記の列のみが主キーとして一緒に定義されているため、私の質問には関係ありません

このアプリケーションでは、このテーブルに大量の挿入を行うため、MERGEを使用せずに次のステートメントを使用します。

INSERT INTO CUSTOMERS  (CUSTOMER_ID , DAY, REGISTERED_TO)                      
                   SELECT ?, ?, ? 
                   FROM DUAL WHERE NOT EXISTS 
                            (SELECT NULL 
                             FROM CUSTOMERS
                             WHERE CUSTOMER_ID = ? 
                               AND DAY = ? 
                               AND REGISTERED_TO = ?
                              )";

バッチ機能を利用したPreparedStatementオブジェクトを使用して、お客様ごとのお申し込みの流れで集めた大量のレコードを挿入します。

問題は、時々次のエラーが発生することです:

ORA-00001: 一意の制約 (CUSTOMERS_PK) に違反しています

奇妙なことは、バッチ挿入を使用せず、各レコードを 1 つずつ (単純に実行して) 挿入すると、エラーが発生しないことですpstmt.execute()

挿入ステートメントに何か問題がありますか?jdbc ドライバー ? バッチ メカニズムを正しく使用していませんか?

これが私の挿入ループの半疑似コードです:

  pstmt = conn.prepareStatement(statement);
  pstmt.setQueryTimeout(90);

  for each customer :
     - pstmt.setObject(1, customer id);
     - pstmt.setObject(2, current day);
     - pstmt.setObject(3, registered to);
     - pstmt.addBatch();
  end for

  pstmt.executeBatch();

これはすべて try/catch/finally ブロックで囲まれており、このプロセスの最後にステートメントと接続が確実に閉じられます。

4

1 に答える 1

2

複数のスレッドまたはプロセスを並行して使用し、それぞれが挿入を行っていると思います。この場合、Oracle のトランザクション分離機能がマージの試行を無効にします。

  • セッション A はステートメントを実行し、行 (x,y,z) を挿入します
  • セッション B は同じステートメントを実行し、行 (x,y,z) を挿入しようとし、ロックを取得して待機します。
  • セッション A のコミット
  • セッション B が「一意の制約違反」エラーを受け取る

これは、セッション A がコミットするまで、セッション B は新しい行を認識しないため、同じ行を挿入しようとするためです。

于 2012-11-27T14:49:12.050 に答える