异常的传递

如果PL/SQL程序在执行的过程中发生了错误,则转去执行相应的异常处理程序,然后结束块的执行。如果没有定义相应的异常处理程序,那么PL/SQL程序将向调用者返回出错的相关信息,也就是把异常传递到程序的调用者,然后结束程序的执行。如果这个程序是在SQL*Plus中执行的,那么异常就会传递到SQL*Plus环境,从而把错误信息显示在屏幕上。

例如:下面的块在检索数据时引发了TOO_MANY_ROWS异常,并把异常传递到SQL*plus中。

  1. DECLARE
  2. name emp.ename%type;
  3. BEGIN
  4. SELECT ename INTO name FROM emp WHERE deptno=10;
  5. EXCEPTION --这里将引发TOO_MANY_ROWS异常
  6. when NO_DATA_FOUND then
  7. dbms_output.put_line('没有满足条件的数据');
  8. END;

这个块的执行结果为:

  1. DECLARE
  2. *
  3. ERROR 位于第1 行:
  4. ORA-01422: exact fetch RETURNs more than requested number of rows
  5. ORA-06512: at line 4

从程序的执行结果可以看出,由于在程序中没有处理异常TOO_MANY_ROWS,所以这个异常被传递到程序的调用者SQL*Plus中。

在PL/SQL块中可以定义过程、函数等形式的子程序, 在每个子程序中也可以分别定义异常处理程序。 这样当子程序执行出现错误时,就转去执行相应的异常处理程序。然后子程序的执行便告结束,PL/SQL块接着从子程序调用处的下一条语句开始执行。如果子程序对出现的异常进行了处理,就可以认为子程序的执行正常结束。

例如:再来考虑子程序重载的这个例子。在这个块中定义了两个重载过程increase_salary,用来对员工增加工资。第一个过程有两个参数,分别是部门编号和增加的额度,用于对指定的部门的员工增加工资。第二个过程带有一个参数,即增加的额度,用于对所有员工增加工资。

这里在第一个过程中添加了处理异常NO_DATA_FOUND的程序,还添加了一条SELECT语句。如果在调用过程时指定了一个不存在的部门,那么在查询该部门信息时将引发NO_DATA_FOUND异常,这个过程的执行流程就会转到异常处理部分。

  1. /* 自定义包的声明 只有在包中存储过程才能重载 */
  2. create or replace package mypackage
  3. is
  4. procedure increase_salary(d_no emp.deptno%type,
  5. amount float);
  6. procedure increase_salary(amount float);
  7. end;
  8. /* 自定义包的实现 */
  9. create or replace package body mypackage
  10. is
  11. procedure increase_salary(d_no emp.deptno%type,
  12. amount float)
  13. is
  14. d_name dept.dname%type; begin
  15. select dname into d_name from dept where
  16. deptno=d_no;
  17. update emp set sal=sal+amount where
  18. deptno=d_no;
  19. commit;
  20. /* 异常处理 */
  21. exception
  22. when NO_DATA_FOUND then
  23. dbms_output.put_line('这个部门不存在');
  24. end;
  25. procedure increase_salary(amount float)
  1. is
  2. begin
  3. update emp set sal=sal+amount;
  4. commit;
  5. end;
  6. end mypackage;
  • /* 主程序 */
  • BEGIN
  • mypackage.increase_salary(100,100.50);
  • mypackage.increase_salary(200);
  • END;
    1. 这个部门不存在

    从块的执行结果可以看出,当调用第一个重载过程时,因为传递了一个不存在的部门编号,所以引发了NO_DATA_FOUND异常。这个过程在处理异常后便执行结束,PL/SQL块接着执行第二条调用语句,调用第二个过程。第一个过程因为处理了出现的异常,所以可以认为是正常结束,它并不会影响块整个程序中其他语句的执行。

    对异常的处理应当遵循“不扩散”的原则。在子程序中发生的错误应该在子程序中进行处理,不要扩散到主程序中。同样,在PL/SQL块的可执行部分出现的错误应该在块中进行处理,不要扩散到调用该块的SQL*Plus或应用程序中。

    如果在子程序中没有处理出现的错误,情况会怎么样呢?再来考虑上面的例子,我们取消了第一个increase_salary过程中的异常处理部分。为了便于测试,我们在两条调用语句中间添加了一条输出语句。

    修改后的代码如下:

    1. create or replace package mypackage01
    2. is
    3. procedure increase_salary(d_no emp.deptno%type,
    4. amount float);
    5. procedure increase_salary(amount float);
    6. end;
    7. create or replace package body mypackage01
    8. is
    9. procedure increase_salary(d_no emp.deptno%type,
    10. amount float)
    11. is
    12. d_name dept.dname%type;
    13. begin
    14. select dname into d_name from dept where
    15. deptno=d_no;
    16. update emp set sal=sal+amount where
    17. deptno=d_no;
    18. commit;
    19. /* 不处理异常 */
    20. end;
    21. procedure increase_salary(amount float)
    22. is
    23. begin
    24. update emp set sal=sal+amount;
    25. commit;
    26. end;
    27. end mypackage01;
    28. /* 主程序 */
    29. BEGIN
    30. mypackage01.increase_salary(100,100.50);
    31. dbms_output.put_line('第一个过程执行结束');
    32. mypackage01.increase_salary(200);
    33. END;

    这个块的执行结果为:

    1. DECLARE
    2. *
    3. ERROR 位于第1 行:
    4. ORA-01403: no data found
    5. ORA-06512: at line 6
    6. ORA-06512: at line 16

    在调用第一个increase_salary过程时,由于指定了一个不存在的部门编号,所以引发了异常NO_DATA_FOUND。在子程序中没有处理这个异常,所以过程非正常结束。从程序的执行结果可以看出,我们指定的输出并没有产生,可以断定,第一条调用语句以下的所有语句都没有得到执行。

    如果子程序没有处理出现的错误,那么异常就被传递到它的调用者,即PL/SQL主程序,从而在主程序中也会产生错误。所以主程序将在调用子程序的地方停止执行,而去处理这个异常。但是因为主程序也没有定义异常处理程序,所以这个异常又被传递到块的调用者—SQL*Plus,从而在屏幕上显示出错的信息。

    从子程序中传递到PL/SQL主程序中的异常,能不能在主程序中进行处理呢?答案是肯定的。如果在主程序中定义了异常处理程序,那么异常被从子程序传递到主程序中后,就像在主程序中产生的异常一样进行处理。这样我们可以在主程序中编写统一的异常处理程序,无论异常是在主程序中抛出的,还是在子程序中抛出的,都可以得到同样的处理。这种做法虽然是可行的,但是它不符合“不扩散”原则。如果程序出现了异常,不容易确定是什么地方出现了错误,也无法对程序的不同部分产生的异常进行单独的处理。例如,把上述例子中第一个increase_salary过程的异常处理放在PL/SQL块中。如果调用过程时引发了异常,便可以进行处理。修改后的代码如下(这里去掉了第二个过程):

    1. create or replace procedure increase_salary(d_no
    2. emp.deptno%type, amount float)
    3. is
    4. d_name dept.dname%type;
    5. BEGIN
    6. SELECT dname INTO d_name FROM dept
    7. WHERE deptno=d_no;
    8. UPDATE emp set sal=sal+amount WHERE
    9. deptno=d_no;
    10. END;
      1. /* 主程序处理异常 */
      2. BEGIN
      3. increase_salary(100,100.50);
      4. EXCEPTION
      5. WHEN NO_DATA_FOUND then
      6. dbms_output.put_line('这是在过程中产生的异常');
      7. END;

      这个块的执行结果为:

      1. 这是在过程中产生的异常

      从上述执行结果可以看出,从子程序中传递到主程序的异常确实可以在主程序中进行处理。但是在主程序中也可能产生同样的异常,这时如果输出同样的信息就不合适了。

      从上面的例子可以看出,在主程序、各个子程序之中可能会因为不同的原因引发同一个异常。为了方便地确定异常产生的原因,应该在PL/SQL程序的每部分都定义异常处理程序。

      异常的传递如果PL/SQL程序在执行的过程中发生了错误,则转去执行相应的异常处理程序,然后结束块的执行。如果没有定义相应的异常处理程序,那么PL/SQL程序将向调用者返回出错的相关信息,也就是把异常传递到程序的调用者,然后结束程序的执行。如果这个程序是在SQL*Plus中执行的,那么异常就会传递到SQL*Plus环境,从而把错误信息显示在屏幕上。例如:下面的块在检索数据时引发了TO 在PLSQL程序开发过程中,很重要的部分就是对程序 异常 的监控和处理,包括如何触发 异常 ,何时进行处理,如何进行处理,是否将程式中的所有 异常 集中在一起,通过公共 异常 处理的procedure或function,如果没有完善的程式处理机制,很难说该程式是一只健壮的程式,当程式遇到很多类型或者量很多资料时,系统若没有 异常 处理必然会导致程式的出错 当预判到了某些 异常 ,需要对预判到的 异常
      WEB应用在连接数据库的时候,有时会抛出违反协议的报错,这种报错正常都是因ojdbc的jar包版本不对导致,也就是说jdbc驱动的版本与 oracle 数据库的版本不一致。 在这种情况下,需要先确认 oracle 的版本然后针对性的去找jdbc的版本,还需要记住一点,jdbc驱动版本不是越高越好,要实际测试看与你的数据库是否匹配(包括hibernate的版本)。 当前测试的环境是 Oracle 11g,jd...
      执行时 异常 传递 执行时 异常 传递 是指在PL/SQL块的执行部分抛出 异常 传递 机制。 1. 如果当前PL/SQL的 异常 处理部分具有一个匹配的 异常 处理器,则执行当前块的 异常 处理器,然后将控制权 传递 到外层语句块。 2. 如果当前PL/SQL块中没有匹配的 异常 处理器,则在当前块中抛出的 异常 会被 传递 到外层的 异常 处理器,然后执行外层语句块的步骤1中的匹配操作。 3. 如果已经到了顶层,没有外层语句块了,则 异常
      1. 报错背景 tomcat + Oracle + Druid连接池后台报错(java.sql.SQLException: 违反协议),很奇怪的是只有某种特殊情况下才报错,项目其他功能都运转正常,报错信息看最后面代码示例。 2. 可能原因(OALL8-jdbc与 Oracle 驱动版本不一致) 1、jdbc配置出错(驱动配置): oracle .jdbc. Oracle Driver
      ORACLE 中使用%TYPE和%ROWTYPE定义变量的类型 一、%TYPE的用法 定义变量时可以使变量与另一个已经定义好的变量(通常是表中的一列)的数据类型保持一致,可以使用%Type的方式。使用%Type定义变量的好处是:当被参照的那个变量的数据类型发生变化时,该变量的数据类型也会随着发生变化,该变量的类型不由自己决定,而是由与之关联的对象决定。 例如:输入一个部门的编号,显示出这个部门的名称...