例外処理後のトランザクションの再試行

例外を呼び出したトランザクションを例外処理後に再試行するには、次の方法を使用します。

  1. 例外処理部を持つサブブロックにトランザクションを入れます。
  2. サブブロックの中で、トランザクションを開始する前にセーブポイントをマークします。
  3. サブブロックの例外処理部の中で、セーブポイントまでロールバックする例外ハンドラを配置し、問題の修正を試行します。
  4. LOOP文の中にサブブロックを配置します。
  5. サブブロックの中で、トランザクションを終了させるCOMMIT文の後にEXIT文を配置します。

    トランザクションの実行に成功すると、COMMIT文とEXIT文が処理されます。

    トランザクションの実行に失敗すると、サブプログラムの例外処理部に制御が移り、例外ハンドラが実行されてからループが繰り返されます。

例12-26 例外処理後のトランザクションの再試行

DROP TABLE results;
CREATE TABLE results (
  res_name   VARCHAR(20),
  res_answer VARCHAR2(3)
);
 
CREATE UNIQUE INDEX res_name_ix ON results (res_name);
INSERT INTO results (res_name, res_answer) VALUES ('SMYTHE', 'YES');
INSERT INTO results (res_name, res_answer) VALUES ('JONES', 'NO');
 
DECLARE
  name    VARCHAR2(20) := 'SMYTHE';
  answer  VARCHAR2(3) := 'NO';
  suffix  NUMBER := 1;
BEGIN
  FOR i IN 1..5 LOOP  -- Try transaction at most 5 times.
 
    DBMS_OUTPUT.PUT('Try #' || i);
 
    BEGIN  -- sub-block begins
 
       SAVEPOINT start_transaction;
 
       -- transaction begins
 
       DELETE FROM results WHERE res_answer = 'NO';
 
       INSERT INTO results (res_name, res_answer) VALUES (name, answer);
 
       -- Nonunique name raises DUP_VAL_ON_INDEX.
 
       -- If transaction succeeded:
 
       COMMIT;
       DBMS_OUTPUT.PUT_LINE(' succeeded.');
       EXIT;
 
    EXCEPTION
      WHEN DUP_VAL_ON_INDEX THEN
        DBMS_OUTPUT.PUT_LINE(' failed; trying again.');
        ROLLBACK TO start_transaction;    -- Undo changes.
        suffix := suffix + 1;             -- Try to fix problem.
        name := name || TO_CHAR(suffix);
    END;  -- sub-block ends
 
  END LOOP;
END;
/

結果:

Try #1 failed; trying again.
Try #2 succeeded.

例12-26では、res_nameの値が一意でない場合に事前定義の例外DUP_VAL_ON_INDEXINSERT文により呼び出されるトランザクションを、前述の方法を使用して再試行しています。