异常处理,SQL编程中的异常处理

图片 11

 

  在程序运行过程中,如果JVM检测出一个不可能执行的操作,就会出现运行时错误。

本文出处:  

  在Java中,运行时错误会作为异常抛出。异常就是一个对象,表示阻止正常进行程序执行的错误或者情况。如果异常没有被处理,那么程序将会非正常终止。

 


 T-SQL编程与应用程序一样,都有异常处理机制,比如异常的捕获与异常的抛出(try
catch throw),本文简单介绍异常捕获与异常抛出在T-SQL编程中的实际使用 。

  异常是从方法抛出的。方法的调用者可以捕获以及处理该异常。

异常处理简单说明

  throw语句的执行称为抛出一个异常。异常就是一个从异常类创建的对象。

异常捕获在应用程序编程中非常常见,提供了处理程序运行时出现的任何意外或异常情况的方法
刚毕业的时候对于异常处理迷茫不解,尤其是catch中又throw,既然catch或者不catch,都会throw,为什么要catch后再throw?catch中到底要做什么处理?
后来接触的多了开始慢慢理解了异常处理这个机制,在应用程序和T-SQL中应该是类似的
可以简单地这样理解:
对于UI层面,
异常捕获,个人理解就是对于可能发生异常的代码段进行捕获处理,给予用户友好的提示信息,
防止应用程序崩溃(或者抛给给用户一个后台代码错误的页面)的一种做法。
如果是底层方法(这个底层可以这么理解A方法调用B方法,B方法又调用C方法,C方法就是底层方法),
异常处理可以是在捕获之后继续抛出给上层调用者,让调用者知道它调用的方法发生了什么问题。
对于发生了异常的代码本身,要记录下来异常的原因,以便于问题的排查。
比如C方法中发生了异常,要告诉调用他的B方法“我发生了异常,异常原因是***”,
这种的话C就要抛出异常,
同时C要记录异常的信息(通过不同方式将上面的异常原因记录下来),供后继排查问题作参考。

  当异常被抛出时,正常的执行流程就被中断。就像它的名字所提示的,“抛出异常”就是将异常从一个地方传递到另一个地方。调用方法的语句包含在一个try块和一个catch块中。

  以上是理论基础,下面以T-SQL中的异常处理为例,简单介绍一下异常处理方式和要做的事情,T-SQL中的异常处理。

  throw语句类似于方法调用,但不同于调用方法的是,它调用的是catch块。从某种意义上讲,catch块就像带参数的方法定义,这些参数匹配抛出的值的类型。但是,它不像方法,在执行完catch块之后,程序控制不再返回到throw语句;而是执行catch块后的下一条语句。

 

  一个异常可能是通过try块中的throw语句直接抛出的,或者调用一个可能会抛出异常的方法而抛出。

catch块中处理异常信息

  使用异常处理的优点:它能使方法抛出一个异常给它的调用者,并由调用者处理该异常。如果没有这个能力,那么被调用方法就必须自己处理异常或者终止该程序。被调用的方法通常不知道在出错的情况下该做什么,这是库方法的一般情况。库方法可以检测出错误,但是只有调用者才知道出现错误时需要做些什么。异常处理最根本的优势就是将检测错误(由被调用的方法完成)从处理错误(由调用方法完成)中分离出来。

  首先借助下面两张表来做示例说明。一张产品信息表,有产品Id和价格信息


  图片 1

  异常时对象,而对象都采用类来定义。异常的根类是java.lang.Throwable。

  下面将通过一个新增产品信息的存储过程说明如何进行异常处理
  如下是新增产品信息的存储过程,存超过程根据参数,插入到Product表中,

  Java API中有很多预定义的异常类。

  图片 2

        图片 3

  在插入数据的过程中,进行了异常捕获,在catch代码中,有两个操作,
  第一步是将异常信息插入ExceptionLog,当然,这个异常信息的格式可以自己定义,第二步抛出异常(throw),就基于上面的理论
  首先为什么要记录异常,这个很容易理解,A写的存储过程给B去调用,B调用的时候发生了异常,将异常信息记录下来有利于A去排查异常的具体原因
  然后抛出异常,目的是告诉B,执行存储过程的时候产生了异常,你的操作并没有成功执行。
  比如下面代码,执行的时候发生了主键冲突异常,throw的作用就是告诉调用者,执行存储过程的时候发生了异常,并将详细的错误信息抛出

  Throwable类是所有异常类的根类。所有的Java异常类都直接或者间接地继承自Throwable。可以通过继承Exception或者Exception的子类来创建自己的异常类。

  当然实际中常见的异常也比较多,比如死锁,主外键冲突,执行超时,没有操作权限等等各种无法估计到的异常,包括记录异常信息的格式,可以自由选择。

  这些异常类可以分为三种主要类型:系统错误、异常和运行时异常。

  图片 4

  系统错误是由Java虚拟机抛出的,用Error类表示。Error类描述的是内部系统错误。这样的错误很少发生。如果发生了,除了通知用户以及尽量稳妥地终止程序外,几乎什么也不能做。

  此时观察Catch中记录的ExceptionLog信息,也记录了下面
  记录下来的异常信息目的是事后排查问题,与上面直接“抛出”的异常信息作用不同的是,
  一个是在异常的发生的时候告诉调用者,抛出异常是标明当前执行的代码发生了异常,ExceptionLog记录异常信息是做排查分析异常原因使用

  异常是用Exception类表示的,它描述的是由程序和外部环境所引起的错误,这些错误能被程序捕获和处理。ClassNotFoundException、IOException

图片 5

  运行时异常是用RuntimeException类表示的,它描述的是程序设计错误,例如,错误的类型转换、访问一个越界数组或数值错误。运行时异常通常是由Java虚拟机抛出的。ArithmeticException、NullPointerException、IndexOutOfBoundsException

  如果不抛出异常,比如如下的代码,注释掉throw语句,等于是在catch块中单纯地记录下来异常之后“吃掉”异常,会出现什么情况

  RuntimeException、Error以及它们的子类都称为免检异常。所有其他异常都称为必检异常,意思是指编译器会强制程序员检查并通过try-catch块处理它们,或者在方法头进行声明。

  图片 6

  在大多数情况下,免检异常都会反映出程序设计上不可恢复的逻辑错误。例如,如果通过一个引用变量访问一个对象之前并未将一个对象赋值给它,就会抛出NullPointerException;如果访问一个数组的越界元素,就会抛出IndexOutOfBoundsException异常,这些都是程序中必须纠正的逻辑错误。免检异常可能在程序的任何一个地方出现。为避免过多地使用try-catch块,Java语言不强制要求编写代码捕获或声明免检异常。

  比如在Product表中已经存在Id =
1的记录的情况下,执行如下代码是失败,
  但是客户端并没有任何提示,调用方并不知道发生了什么,调用存储过程的时候到底是失败了还是成功了?没有一个明确的答案。
  这就是catch中吃掉异常的后果。
  因此正常情况下,catch中记录完异常之后,要“抛出”异常,当异常发生的时候,明确地告诉调用方发生了什么问题。


  图片 7

  异常的处理器是通过从当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到的。

 

  Java的异常处理模型基于三种操作:声明一个异常、抛出一个异常和捕获一个异常。

使用throw显式抛出异常

  声明异常

  某些情况下需要主动抛出异常的方式来中断逻辑的执行,什么意思?
  就是说当前的逻辑,只有在满足一定的条件下才能执行,如果条件不满足,就要明确告诉调用方,你的条件不满足,当前逻辑无法正常执行。
  举个例子,还是刚才插入产品信息的存储过程,如下
  当插入产品信息的时候,只有产品价格大于0才是有效的产品信息,否则无法插入,
  此时就可以通过抛出异常的方式明确地告诉调用者(当然也有其他方式),你的参数不合法,使用throw抛出自定义异常,强制中断代码的执行。

  在Java中,当前执行的语句必属于某个方法。Java解释器调用main方法开始执行一个程序。每个方法都必须声明它可能抛出的必检异常类型。这称为声明异常。因为任何代码都可能发生系统错误和运行时错误,因此,Java不要求在方法中显示声明Error和RuntimeException
(免检异常)。但是,方法要抛出的其他异常都必须在方法头中显示声明,这样,方法的调用者会被告知有异常。

  图片 8

  如果方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常。

  上面的方式只是举个示例说明,正常情况下,调用方传递过来的参数都是经过校验的,不会发生太低级的错误。
  当然,实际应用中应该比这个复杂的多,无法保证调用者都是从用户图形界面(UI)发起的,也就是说无法通过直接的预先处理来确保输入信息的合法性
  对于类似上面的存储过程
  首先无法保证调用方永远传递过来的参数是合法有效的,其次连调用者也无法确保自己的生成的参数的逻辑永远不会发生错误。
  此时对于逻辑上要求非常严谨的程序来说,就需要做类似上面主动抛出异常了来中断代码的执行了。
  当然上面问题的处理方式也不止这一种,只是说异常处理的应用场景和方式。

  抛出异常

  

  检测到错误的程序可以创建一个适合的异常类型的实例并抛出它,这就称为抛出一个异常。

throw语句的使用

    IllegalArgumentException ex = new IllegalArgumentException
(“Wrong Argument”);

  最后说一下throw语句,

    throw ex;

  图片 9

    或者,根据偏好,也可以使用下面的语句:

  throw是必raiserror更加方便和直观的异常抛出方式,也是推荐的异常处理方式,具体差异网上一大把就不多说了
  throw有两种使用方式,抛出自定义异常和直接在catch块中抛出异常。
  抛出自定义异常的时候有三个必须参数,下面会细说,catch块中可以直接用throw不需要任何参数的方式抛出捕获到的异常
  throw语句的前一句需要一分号结尾,前一句又不能保证一定有分号,
  所以可以直接把分号写在throw的前面,比如文中的;throw 50000,’Price can
not be less than 0′,1 写法
  当抛出自定义错误的时候,throw语句有三个参数,参考如下
  throw语句安全级别默认为16并且不会被修改,换句话说就是throw语句执行之后将抛出错误,打断当前Session的批处理语句,throw后面的语句将不会执行
  第一个参数是错误号,用户自定义错误号要大于50000(50000 to
2147483647)
  第二个参数是自定义错误信息内容,可以根据需要自定义
  第三个参数是Error State,他的作用是可以标记异常发生的位置,
     比如同样是“参数不能小于0”的错误提示,整个存储过程中有可能有两个地方进行同样的校验
       就可以在两个地方使用Error State不同分别来抛出异常;
       throw 50000,’Price can not be less than 0′,1
       throw 50000,’Price can not be less than 0′,2,
       因为Error State不同,就可以根据具体的Error
State更加方便地知道是哪个地方发生了异常,参考

    throw new IllegalArgumentException (“Wrong Argument”);

  

  声明异常的关键字是throws,抛出异常的关键字是throw。

throw语句存储自定义异常到messages系统表
可以将自定义的异常信息加入到sys.messages
系统表中,先加英文的,再加中文的。
然后使用的时候可以使用这个预定义的message,而不是每次写一个字符串

  捕获异常

EXEC sp_addmessage   
    @msgnum = 50001,   
    @severity = 16,   
    @msgtext = N'%s can not be less than 0',   
    @lang = 'us_english';  

EXEC sp_addmessage   
    @msgnum = 50001,   
    @severity = 16,   
    @msgtext = N'%s 不能小于0',   
    @lang = '简体中文';  

  如果在执行try块的过程中没有出现异常,则跳过catch子句。

如下,存储过程中先通过FORMATMESSAGE来格式化异常信息,然后使用throw
50001, @msg, 1;的方式抛出异常

  如果try块中的某条语句抛出一个异常,Java就会跳过try块中剩余的语句,然后开始查找处理这个异常的代码的过程。处理这个异常的代码称为异常处理器;可以从当前的方法开始,沿着方法调用链,按照异常的反向传播方向找到这个处理器。从第一个到最后一个逐个检查catch块,判断在catch块中的异常类实例是否是该异常对象的类型。如果是,就将该异常对象赋给所声明的变量,然后执行catch块中的代码。如果没有发现异常处理器,Java会退出这个方法,把异常传递给调用这个方法的方法,继续同样的过程来查找处理器。如果在调用的方法链中找不到处理器,程序就会终止并且在控制台上打印出错信息。寻找处理器的过程称为捕获一个异常。P393

图片 10

  从一个通用的父类可以派生出各种异常类。如果一个catch块可以捕获一个父类的异常对象,它就能捕获那个父类的所有子类的异常对象。

  然后在调用存储过程是的时候,如下是异常被触发时候的抛出自定义异常的场景。

  在catch块中异常被指定的顺序是非常重要的。如果父类的catch块出现在子类的catch之前,就会导致编译错误。

  图片 11

  Java强迫程序员处理必检异常,如果方法声明了一个必检异常(即Error或RuntimeException之外的异常),就必须在try-catch中调用它,或者在调用方法中声明要抛出异常。

  

  从异常中获取信息

总结:本文简单介绍了T-SQL编程中的异常处理方式,异常处理的时候可以通过捕获异常信息并记录下来,确保代码的维护性,也为问题排查提供参考依据。
    
也可以在确保满足业务逻辑的条件下,主动抛出自定义异常的方式终止代码的执行,来确保业务逻辑的正确性。

  异常对象包含关于异常的有价值的信息。可以利用下面这些java.lang.Throwable类中的实例方法获取有关异常的信息。printStackTrace()方法在控制台上打印栈跟踪信息。getStackTrace()方法提供编程的方式,来访问由printStackTrace()打印输出的栈跟踪信息。

    

  在异常事件中,执行仍然会继续。如果处理器没有捕获到这个异常,程序就会突然中断。

 


finally子句

  无论异常是否产生,finally子句总是会被执行。

  有时候,不论异常是否出现或者是否被捕获,都希望执行某些代码。Java有一个finally子句,可以用来达到这个目的。

  在任何情况下,finally块中的代码都会执行,不论try块中是否出现异常或者是否被捕获。考虑下面三种可能的情况:

  (1)如果try块中没有出现异常,执行finalStatements,然后执行try语句的下一条语句。

  (2)如果try块中有一条语句引起异常,并被catch捕获,然后跳过try块的其他语句,执行catch块和finally子句。执行try语句之后的下一条语句。

  (3)如果try块中有一条语句引起异常,但是没有被任何catch块捕获,就会跳过try块中的其他语句,执行finally子句,并且将异常传递给这个方法的调用者。

  即使在到达finally块之前有一个return语句,finally块还是会执行。


何时使用异常

  当错误需要被方法的调用者处理的时候,方法应该抛出一个异常。

  try块包含正常情况下执行的代码。catch块包含异常情况下执行的代码。异常处理将处理错误的代码从正常的程序设计任务中分离出来,这样,可以使程序更易读、更易修改。但是,应该注意,由于异常处理需要初始化新的异常对象,需要从调用栈返回,而且还需要沿着方法调用链来传播异常(这里注意方法调用的方向和异常传播的方向是相反的)以便找到它的异常处理器,所以,异常处理通常需要更多的时间和资源。

  异常出现在方法中。如果想让该方法的调用者处理异常,应该创建一个异常对象并将其抛出。如果能在发生异常的方法中处理异常,无须抛出异常。


重新抛出异常

  如果异常处理器不能处理一个异常,或者只是简单地希望它的调用者注意到该异常,Java允许该异常的处理器重新抛出异常。

try{
    statements;
}
catch(TheException ex)
{
    perform operation before exits;
    throw ex;  
}

  语句throw
ex重新抛出异常给调用者,以便调用者的其他处理器获得处理异常ex的机会。


创建自定义异常

  可以通过派生java.lang.Exception类来定义一个自定义异常类

  如果遇到一个不能用预定义异常恰当描述的问题,那就可以通过派生Exception类或其子类,例如IOException,来创建自己的异常类。


 

Leave a Comment.