---
title: "异常处理 - OceanBase 数据库 V4.3.4 | OceanBase 文档中心"
description: 异常处理 异常处理注意事项 OceanBase 推荐只在下列情形里使用异常处理程序： 您预计可能有异常，并期望处理它。 比如说，您预计最终一个 SELECT INTO 语句可能会返回空行，导致 OceanBase 上报一个预定义异常 NO_DATA_FOUND 。您期望通过子程序或者 STORED PROCEDURE…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 4.3.4

# 异常处理

更新时间：2024-11-20 23:00:00

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V4.3.4/zh-CN/700.reference/400.database-design-specifications-and-practices/700.exception-handling.md)  

## 异常处理注意事项

OceanBase 推荐只在下列情形里使用异常处理程序：

- 您预计可能有异常，并期望处理它。

  比如说，您预计最终一个 SELECT INTO 语句可能会返回空行，导致 OceanBase 上报一个预定义异常 `NO_DATA_FOUND`。您期望通过子程序或者 `STORED PROCEDURE` 块处理这个异常（这不是错误），然后让程序能继续执行下去。示例如下：

  ```shell
  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;

  ```
 - 您必须放弃或关闭一个资源。示例如下：

  ```shell
  ...
  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);
  ...

  ```
 - 在子程序代码的顶层，您需要记录错误日志。示例如下：

  ```shell
  BEGIN
  proc(...);  -- 调用其他子程序
  EXCEPTION
  WHEN OTHERS THEN
  log_error_using_autonomous_transaction(...); -- 使用自治事务记录日志
  RAISE; -- 继续上抛异常.
  END;
  /

  ```

## 异常处理示例

OceanBase 提供了两种异常处理机制。一种是在 SQL 语句编写时，通过 PL 中的 exception 进行异常捕获，另一种是应用层代码开发时，使用不同的驱动会有对应的异常处理方式。

### PL 异常处理

PL 异常错误包含系统内部异常、预定义异常和用户定义异常三种类型。针对这 3 种类型，如下分别给出示例。

- 系统内部异常。

  示例：通过命名捕获系统内部异常。

  ```shell
  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!

  ```
 - 预定义异常。

  ```shell
  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

  ```
 - 用户自定义异常，分为自定义异常和自定义错误码。

     - 自定义异常。

      ```shell
      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

      ```
     - 自定义错误码。

      ```shell
      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`，参考如下示例。

```shell
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 驱动为示例。其他更多的不同的驱动对异常的处理方式请从对应的驱动官网进行学习。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
