首批通过分布式安全可靠测评,为关键业务系统打造
异常处理
更新时间:2024-11-20 23:00:00
异常处理注意事项
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); -- then you want to close the file. RAISE; -- 继续上报异常. END; UTL_FILE.FCLOSE(file); ...在子程序代码的顶层,您需要记录错误日志。示例如下:
BEGIN proc(...); -- 调用其他子程序 EXCEPTION WHEN OTHERS THEN log_error_using_autonomous_transaction(...); -- 使用自治事务记录日志 RAISE; -- 继续上抛异常. END; /
异常处理示例
OceanBase 提供了两种异常处理机制。一种是在 SQL 语句编写时,通过 PL 中的 exception 进行异常捕获,另一种是应用层代码开发时,使用不同的驱动会有对应的异常处理方式。
PL 异常处理
PL 异常错误包含系统内部异常、预定义异常和用户定义异常三种类型。针对这 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 为空时,触发这个异常 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 异常处理的内容,请参见官网 《 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 driver。
这里仅以 OceanBase JDBC 驱动为示例。其他更多的不同的驱动对异常的处理方式请从对应的驱动官网进行学习。