例外処理の注意点
OceanBaseでは、例外処理プログラムは以下の状況でのみ使用することを推奨します:
例外が発生する可能性があり、その処理を期待している場合。
例えば、最終的にSELECT INTOステートメントが空の行を返す可能性があり、OceanBaseが定義済みの例外
NO_DATA_FOUNDを報告することを予想している場合です。サブルーチンまたはSTORED PROCEDUREブロックを使用してこの例外(これはエラーではありません)を処理し、プログラムが実行を続けられるようにしたい場合です。例:create or replace procedure select_dept( num_deptno in number,--inモード変数を定義し、部門番号の入力を要求します var_dname out dept.dname%type,--outモード変数を定義し、部門名を格納して出力できます var_loc out dept.loc%type) is begin select dname,loc into var_dname,var_loc from dept where deptno = num_deptno;--特定の部門番号の部門情報を検索 exception when no_data_found then --selectステートメントがレコードを返さない場合 dbms_output.put_line('該当する部門番号は存在しません');--情報を出力 end select_dept;リソースを放棄または閉じる必要がある場合。例:
... file := UTL_FILE.OPEN ... BEGIN statement statement]... EXCEPTION WHEN OTHERS THEN UTL_FILE.FCLOSE(file); -- その後、ファイルを閉じたい場合。 RAISE; -- 例外を引き続き報告します。 END; UTL_FILE.FCLOSE(file); ...サブルーチンコードのトップレベルで、エラーログを記録する必要があります。例:
BEGIN proc(...); -- 他のサブルーチンを呼び出します EXCEPTION WHEN OTHERS THEN log_error_using_autonomous_transaction(...); -- 自律トランザクションを使用してログを記録 RAISE; -- 例外を引き続きスローします。 END; /
例外処理の例
OceanBaseは2種類の例外処理メカニズムを提供しています。1つはSQL文を作成する際に、PL中のexceptionを用いて例外をキャッチする方法であり、もう1つはアプリケーション層のコード開発時に、異なるドライバーごとに対応する例外処理方式を利用する方法です。
PL例外処理
PL例外エラーには、システム内部例外、定義済み例外、ユーザー定義例外の3種類が含まれます。これら3種類について、以下にそれぞれ例を示します。
システム内部例外。
例:ネーミングキャプチャを使用してシステム内部例外を処理します。
obclient>CREATE TABLE dept( dept_id NUMBER(10,0), dname VARCHAR(15), loc VARCHAR(20), CONSTRAINT pk_dept PRIMARY KEY(dept_id) ); Query OK, 0 rows affected (0.07 sec) obclient>INSERT INTO dept VALUES (100,'ACCOUNTING','Los Angeles'),(110,'OPERATIONS','CHICAGO'), (111,'SALES','NEW YORK'); obclient>DECLARE DUPLICATED_DEPT_ID EXCEPTION; PRAGMA EXCEPTION_INIT(DUPLICATED_DEPT_ID, -5024); BEGIN UPDATE dept SET dept_id=110 where dept_id=100; EXCEPTION WHEN DUPLICATED_DEPT_ID THEN DBMS_OUTPUT.PUT_LINE('Duplicated Department ID!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END; / Query OK, 0 rows affected (0.03 sec) Duplicated department id!定義済み例外。
obclient>CREATE TABLE employees( empno NUMBER(4,0), empname VARCHAR(10), job VARCHAR(10), deptno NUMBER(2,0), salary NUMBER(7,2), CONSTRAINT PK_emp PRIMARY KEY (empno) ); Query OK, 0 rows affected (0.07 sec) obclient>INSERT INTO employees VALUES (200,'Jennifer','AD_ASST',1,15000), (202,'Pat','MK_REP',3,12000),(113,'Karen','PU_CLERK', 4,null),(201,'Michael','MK_MAN',3,9000); Query OK, 4 rows affected (0.06 sec) Records: 4 Duplicates: 0 Warnings: 0 obclient>DECLARE v_empid employees.empno%TYPE; v_sal employees.salary%TYPE; BEGIN v_empid := 100; SELECT salary INTO v_sal FROM employees WHERE empno=v_empid; IF v_sal<=10000 THEN UPDATE employees SET salary=salary+100 WHERE empno=v_empid; DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated'); ELSE DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- 従業員番号v_empidが存在しない場合、NO_DATA_FOUND例外がトリガーされます DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found'); WHEN TOO_MANY_ROWS THEN -- 従業員番号v_empidが複数ある場合、TOO_MANY_ROWS例外がトリガーされます DBMS_OUTPUT.PUT_LINE('Duplicated id: '||v_empid); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END; / Query OK, 0 rows affected (0.06 sec) Employee id 100 not foundユーザー定義例外は、カスタム例外とカスタムエラーコードに分かれます。
カスタム例外。
obclient>DECLARE v_empid employees.empno%TYPE; v_sal employees.salary%TYPE; -- 1.例外名SALARY_NOT_SETを定義 SALARY_NOT_SET EXCEPTION; BEGIN v_empid := 113; SELECT salary INTO v_sal FROM employees WHERE empno=v_empid; IF v_sal<=10000 THEN UPDATE employees SET salary=salary+100 WHERE empno=v_empid; DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated'); ELSIF v_sal is NULL THEN -- 2. v_salがNULLの場合、この例外がトリガーされます RAISE SALARY_NOT_SET; ELSE DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Employee id '||v_empid||' not found'); -- 3. SALARY_NOT_SET例外を処理 WHEN SALARY_NOT_SET THEN DBMS_OUTPUT.PUT_LINE('Salary not set: '||v_empid); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END; / Query OK, 0 rows affected (0.07 sec) Salary not set: 113カスタムエラーコード。
obclient>DECLARE v_empid employees.empno%TYPE; v_sal employees.salary%TYPE; BEGIN v_empid := 103; SELECT salary INTO v_sal FROM employees WHERE empno=v_empid; IF v_sal is NULL THEN -- エラー20999をスロー RAISE_APPLICATION_ERROR(-20999, 'The salary of employee is not found'); ELSIF v_sal<=1500 THEN UPDATE employees SET salary=salary+100 WHERE empno=v_empid; DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' updated'); ELSE DBMS_OUTPUT.PUT_LINE('Employee '||v_empid||' ignored'); END IF; END; / OBE-20999: The salary of employee is not found
PL例外処理の詳細については、公式Webサイトの「PLリファレンス」にある「PL例外処理」の章を参照してください。
OceanBase JDBCドライバーの使用例
基盤となるドライバーのエラータイプに直接依存するSQL文については、ORMフレームワークで標準的なJDBCTemplate / SqlMapClientTemplate(SqlMapClientDaoSupport)などの標準テンプレートクラスを統一的に使用することを推奨します。これらの実装は、基盤となるエラーをSpringフレームワークの標準例外タイプに変換します。標準テンプレートクラスを使用している場合、基盤となるドライバータイプcom.alipay.oceanbase.obproxy.mysql.exceptions.jdbc4.MySQLIntegrityConstraintViolationExceptionを直接感知する必要はありません。以下の例を参照してください。
try {
// ビジネスロジック
} catch (Exception e) {
if (e instanceof DataIntegrityViolationException
&& ((DataIntegrityViolationException) e).contains(DuplicateKeyException.class)) {
// 一意性制約違反例外処理
}
}
また、他のドライバー例外com.alipay.oceanbase.obproxy.mysql.exceptions.*も確認する必要があります。使用している場合は、それぞれ対応するSpring標準例外に調整する必要があります。
説明
MySQLモードでは、MySQLデータベースのネイティブJDBCドライバーの使用を推奨します。
ここでは、OceanBase JDBCドライバーを例としています。その他のさまざまなドライバーにおける例外処理方法については、各ドライバーの公式Webサイトで学習してください。