博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
事务和异常易出现的错误
阅读量:4519 次
发布时间:2019-06-08

本文共 4262 字,大约阅读时间需要 14 分钟。

1、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;

转载于:https://www.cnblogs.com/yechanglv/p/6923167.html

你可能感兴趣的文章
20151024-1025-威海-第5届全国高校软件工程专业教育年会参会总结
查看>>
Airplace平台
查看>>
TinyOS实例介绍
查看>>
15个nosql数据库
查看>>
css hack 尽我所见
查看>>
[转]ORACLE联机日志文件无故全部消失
查看>>
Javascript基础学习12问(四)
查看>>
[原]VS2012入门图文教程——第一个程序Hello World
查看>>
#pragma once 与 #ifndef 解析(转载)
查看>>
swift 数据存储
查看>>
Objective-C内存管理教程和原理剖析(三)
查看>>
最大子数组
查看>>
pyton random 模块
查看>>
.bat以管理员身份运行
查看>>
如何用3升和5升桶量取4升水?
查看>>
部署kubernetes1.8.3高可用集群
查看>>
1017
查看>>
C++中数字和字符串的转换
查看>>
eclipse -- git 提示
查看>>
C#调用exe工作目录
查看>>