• delphi的异常及事务保护的常见问题浅析


    、exit方法

      原以为exit方法执行后,会马上退出过程,但是真正做了一个例子来测试后,才让我改变了想法。请看下面这
    个例子,flag最后被赋值为´c´。

    ================================================================================================
      var
        flag: string;
      begin
        try
          flag := ´a´;

          exit;

          flag := ´b´;
        finally
          flag := ´c´;
        end;

        flag := ´d´;
      end;

    ================================================================================================
      分析:不论try子句如何结束,finally 子句总是被执行。(多谢ylmg网友)

    2、一个能让整个系统停止运作的小问题

      在数据库系统设计中,经常使用事务操作来保证数据的完整性,但是设计不当,却容易产生比较大的影响,下面举个例子说明虽然数据完整性保证了,但是可能令到系统完全停止运作:
      ================================================================================================
      adoconnection1.begintrans;
     
      try
       
        ...

        if application.messagebox(´是否确定删除?´, ´询问´, mb_yesno+mb_iconquestion)<>idyes then //(1)
        begin

          ...

        end;

        application.messagebox(´操作失败´, ´警告´, mb_ok);  //(2)

        adoconnection1.committrans;
      except
        application.messagebox(´操作失败´, ´警告´, mb_ok);  //(3)

        adoconnection1.rollbacktrans;
      end;
        ================================================================================================

      分析:上面代码中的问题都是由于(1)、(2)、(3)的application.messagebox引起,但是引起问题的并不是application.messagebox本身,而是它将程序挂起,需要用户干预后,才继续执行后面的操作;如果用户这时候离开了计算机,或者没有对这些对话框进行确定操作的话,可想而知,整个系统因为这个事务没有结束而通通处于等待状态了。

      为了避免这个问题,原则有两个:
      (1)、事务启动后,无需用户干预,程序可以自动结束事务;
      (2)、在事务里面做时间最短的操作。


    3、try...except...end结构

      下面举个例子来说明try结构,还是使用事务操作的例子:

      有问题的代码:
        ================================================================================================
      try
        ...

        adoconnection1.begintrans;

        ...

        adoconnection1.committrans;
      except
        adoconnection1.rollbacktrans;
      end;
        ================================================================================================

      分析:如果try之后到adoconnection1.begintrans这段代码中出现异常,将跳转到adoconnection1.rollbacktrans执行,但是adoconnection1因为出错并没有启动事务,所以adoconnection1.rollbacktrans执行时出错了。

      正确的代码:  ================================================================================================
      adoconnection1.begintrans;
      try
        ...

        ...

        adoconnection1.committrans;
      except
        adoconnection1.rollbacktrans;
      end;
        ================================================================================================

      总之,try的架构是用来保护异常的操作的,try...except之间的产生异常都会执行except...end之间的操作,设计try命令时一定要注意架构的合理性。

    4、欺骗了自己的事务保护

      在做数据库应用软件时,我们经常需要碰到下面的问题:对原有数据进行判断,然后做出相应的修改。这个问题看似比较简单,但是如果考虑到网络上还有别的人在使用同一个系统,那么,你就不的不考虑可能被意外改变的问题了。我的同事比较粗心,虽然在我的提示下考虑了多用户问题,但是他还是写下了有问题的代码:
        ================================================================================================
      var
        adstemp: tadodataset;
        isok: boolean;
      begin
        adstemp := tadodataset.create(self);
     
        try
          adstemp.connection := adoconnection1;
          adstemp.commandtext := ´select fid, fnumber from tb1 where fid=120´;
          adstemp.open;

          isok := adstemp.fieldbyname(´fnumber´).asinteger>100;
        finally
          adstemp.free;
        end;

        if not isok then
          exit;

        adoconnection1.begintrans;
        try
          adoconnection1.execute(´update tb1 set ffull=ffull + 1 from tb1 where fid=120´;
          ...
          ...

          adoconnection1.committrans;
        except
          adoconnection1.rollbacktrans;
        end;
      end;
        ================================================================================================
      分析:不知大家看出问题来了没有,在adoconnection1.begintrans之前判断数据,然后使用adoconnection1.execute改变数据,如果这个数据是共享的,那么在判断之后到adoconnection1.begintrans之前的这段时间里头,tb1的数据可能已经发生了改变,这个事务保护是没有用处的。

      正确的方法是判断和修改必须是同一份数据,下面示例了有两个方法(区别在于启动事务的位置不相同):
     
      代码1(使用事务保护以后,判断的和修改的是同一数据):
      ================================================================================================
      var
        adstemp: tadodataset;
        isok: boolean;
      begin
        adoconnection1.begintrans;
        try
          adstemp := tadodataset.create(self);
     
          try
            adstemp.connection := adoconnection1;
            adstemp.commandtext := ´select fid, fnumber, ffull from tb1 where fid=120´;
            adstemp.open;

            if adstemp.fieldbyname(´fnumber´).asinteger>100 then
            begin
              adstemp.edit;
              adstemp.fieldbyname(´ffull´).asinteger := adstemp.fieldbyname(´ffull´).asinteger + 1;
              adstemp.post;
            end;

          finally
            adstemp.free;
          end;

          adoconnection1.committrans;
        except
          adoconnection1.rollbacktrans;
        end;
      end;
      ================================================================================================

      代码2(使用异常捕捉,假如判断和修改的不是同一份数据,adstemp.post时会出现异常,这个是adodataset对象具有的特性):
        ================================================================================================
      var
        adstemp: tadodataset;
        isok: boolean;
      begin
        adstemp := tadodataset.create(self);
        try
          adstemp.connection := adoconnection1;
          adstemp.commandtext := ´select fid, fnumber, ffull from tb1 where fid=120´;
          adstemp.open;

          if adstemp.fieldbyname(´fnumber´).asinteger>100 then
          begin
     
            adoconnection1.begintrans;
            try
              adstemp.edit;
              adstemp.fieldbyname(´ffull´).asinteger := adstemp.fieldbyname(´ffull´).asinteger + 1;
              adstemp.post;

              adoconnection1.committrans;
            except
              adoconnection1.rollbacktrans;
            end;
          end;
        finally
          adstemp.free;
        end;
      end;
       

  • 相关阅读:
    ESAPI = Enterprise Security API
    WingIDE中调试GAE(google app engine)
    C# 发送Http请求 WebClient类
    [转]使用Google App Engine Helper for Django
    装上Window7
    最近遇到个关于接口的奇怪的问题
    JNDI概述(转载)
    Google App Engine 中通过自定义Django的filter解决时区问题
    C# string与byte[]互转
    Python天天美味(33) 五分钟理解元类(Metaclasses)[转]
  • 原文地址:https://www.cnblogs.com/jiangyuxuan/p/968309.html
Copyright © 2020-2023  润新知