delphi2007 教程

delphi2007 教程

首页 新随笔 联系 聚合 管理
  1013 Posts :: 0 Stories :: 28 Comments :: 0 Trackbacks
经典的判断数据库连接断开问题,翻遍了许多地方没有答案。 Delphi / Windows SDK/API
http://www.delphi2007.net/DelphiDB/html/delphi_20061215202145283.html
程序正常连接状态下,服务器重启、或网络断开等原因将导致数据库连接非正常断开,这时ADOConnection1.Connected、ADOConnection1.State都显示是连接状态(分别为True和stOpen),但其实连接已经断开,服务器、网络正常后一样,目前唯一的办法是关闭并重开程序,这显然不人性化。  
   
  每次查询数据不经判断连接状态都来一次重新连接数据库明显不现实。  
   
  请问,如何判断AdoConnection是否失去了连接?好让我们重新连接数据库:  
      ADOConnection1.Connected   :=   False;  
      ADOConnection1.Connected   :=   True;  
 

try  
          ADOConnection1.Connected   :=   False;  
          ADOConnection1.Connected   :=   True;  
  except  
          有问题  
  end;

每次查询数据不经判断连接状态都来一次重新连接数据库明显不现实。  
   
  楼上这样不就是每次都重新连接数据库吗。

重点在于如何“判断”ADOConnection1.Connected   =   True但AdoConnection其实已经失去了连接?

用timer   刷,  
   
  动态创建   TADOConection   ,设置   ConnectionString   ,然后   Connected   :=   True;  
   
  异常判断就可以!  
   
  发分吧!!!

function   ConnectAppServer:   boolean;   //判断是否连接上了应用服务器  
  begin  
      Result   :=   True;  
      ADOConn.Connected   :=false;  
      try           //测试连接  
          ADOConn.Connected   :=   true;  
      except   //未连接上  
          Result   :=   False;  
      end;  
  end;  
   
  调用时:  
  if   ConnectAppServer   then  
      begin  
          进行处理功能;  
      end  
  else  
      begin  
          showmessage('对不起!数据库失去连接');  
          进行失败处理;  
      end;  
 

在查询的时候用  
  try  
  except  
  重连  
  end;  
 

楼上几位都是每次都要重新连接数据库才能判断出来,这样做不理想,起码响应速度慢。  
   
  就怕你们这么答,我才事先声明了,可能你们急着要分漏了看题?:(  
   
  如能解决好这个问题,我再加200分。  
 

需要时连接,平时断开

你实时去判断他断没有断开,是会耗费大量的网络资源的.

ADOConnection1BeforeDisconnect事件  
  或者  
  ADOConnection1AfterDisconnect事件  
  或者  
  ADOConnection1Disconnect事件来判断出发

楼上的有创意,学习了

我怎么感觉事件一点用都没有!?

你可以判断数据库的端口是否开放,如果返回false说明通讯有中断...

可以先执行操作,如果出现操作异常时,判断是否是因为数据库连接失败引起的,再重新执行你所要执行的操作了。

这种问题根本就没有讨论的价值  
   
  1、如果网络真的断开了,这个时候不管你的程序判断结果是什么,你都得关闭程序  
  2、如果没有断开,系统会自动尝试去连接的  
   
  如果每次都判断,那会造成系统很大的负担。

一般是选   去   “干事”   要是出错了,查一个出错类型才会知是网络断了  
   
  当然某些客户端很久用一次的,但又需要显示连接状态的,做个心跳查询定时检测下连接也是有必要的,只不过时间设长一点。。。

Hank(星星农场)  
  ----  
  这种问题根本就没有讨论的价值  
   
  1、如果网络真的断开了,这个时候不管你的程序判断结果是什么,你都得关闭程序  
  2、如果没有断开,系统会自动尝试去连接的  
   
  如果每次都判断,那会造成系统很大的负担。  
  ----  
   
  Hank(星星农场)的回答还有些许切题,不过也是没心思认真看题。  
   
  1、网络彻底断开了,还用问你干嘛,如来佛祖都没办法。  
  我写的是“服务器、网络正常后一样”,ADOConnection1显示是连接状态,但其实连接已经断开。  
   
  2、系统会自动尝试去连接的,系统如何去自动尝试?  
   
  3、如果每次都判断,那会造成系统很大的负担。的确是这么回事。  
   
  麻烦各位都认真看下题。

我认为   Hank(星星农场)   的观点是值得参考的  
  判断网络联接是否正常要看实际的应用程序中是否需要,而且如果用timer等来断开联接是非常耗费资源的.  
  我有个想法,假设我们认为联接是正常的,每次从数据库系统表中查询:  
  在sqlserver中可以select   sysdate,  
  在oracle中可以   select   sysdate   from   dual  
  如果不出错,则我们就认为是正常的,如果出错,刚关闭联接,再联接,你们觉得如何  
 

说明,我已经仔细看过题目了,也知道你的意思  
   
  1、系统运行的好好的,服务器重起,这个时候客户端是没有提示的,但是当客户端进行数据库相关操作时系统会提示断开,你想这个时候能给客户一个提示是没有意义的,因为在实际过程中如果重起要么管理员提前通知要么电话去问。  
   
  2、重起完后如果进行系统数据操作,系统会自己尝试去连接的,这不是自动连接吗?  
   
  3、如果真的要判断,那只有单独建立一个ADOConnection去尝试连接数据库,这样就太罗嗦了  
   
  4、如果你的应用是那种如果服务器连不上数据暂时保存在本地的玩法最好通过事务处理去做

前几天的系统中也出现了这样的需求.  
  试了一些办法:  
  ...............  
  1.用你的连接执行一个简单的查询操作,如select   getdate()看有无返回值.  
  2.采用多线程的方式,用一个ADOConnection专门来判断网络.并给系统一个全局变量来记录网络状态,在操作时只需判断变量状态值.  
  3.采用Indy方式,客户机和服务器分别放一个.客户机每发一个消息出去后,若服务器接收到则马上返回一个值.如客户机发一个"1+1"的消息,服务器马上返回"2".当然里边要加一些你自己的约定格式串,不然客户机多了分不清.在指定的时间内没接收到返回值的话说明网络已经出现故障.

基本上所有的系统都会面临这种问题的,我的解决办法是:  
   
  定义一个Timer,定期的执行一个最简单的查询操作,比如Select   SYSDATE   from   DUAL   根据结果判断是否断线,然后ReConnect

楼主你要知道,你又要判断是否连接,又不允许人家刷新检测,我觉得是不对的.  
  我估计你是怕刷新检测时时间太长,那么,你可以单独设置一个adoConnection,把它的timeout设置短一点,然后用一个数据集查询如:   select   1   as   testnum  
  如果出错就说明连接可能有问题了.  
   
 

你如果想靠检测网络是否连接来判断数据库连接是否正常的话,那是不行的.  
  你可以试一下:把网络拔了,马上又插上.1秒钟绝对能搞定,但数据库却断开了连接.

感觉没有好的办法.  
   
  因为网络断开也不是一个就能确定的.就像楼上所说的那样.   windows系统也不能一下就确定网络连接不上.   还需要判断一会的时间,   就像本地连接那样.   把网线拨了,   也不是立即就显示断开了.   还要在那动一会就提示   "   网络连接失败   "

请大家注意我的重点:  
   
  如何不通过查询来判断AdoConnection是否失去了连接,通过状态属性什么的。

我的一个想法,不知道可不可啊?  
  一般我们都是用  
  Try  
  except  
  end  
  来捕捉异常的。  
  自定义一个异常来显示不能与数据库连接???  
  每当有这个异常的时间,就自动重新连接数据库就可以了??  
  这样就不用刷新判断了??只有出现这个连接不上数据库的时候才会重连接了

GoldShield(李柏岑)   (   )   信誉:106         Blog     2006-12-22   13:34:48     得分:   0      
     
     
         
  前几天的系统中也出现了这样的需求.  
  试了一些办法:  
  ...............  
  1.用你的连接执行一个简单的查询操作,如select   getdate()看有无返回值.  
  2.采用多线程的方式,用一个ADOConnection专门来判断网络.并给系统一个全局变量来记录网络状态,在操作时只需判断变量状态值.  
  3.采用Indy方式,客户机和服务器分别放一个.客户机每发一个消息出去后,若服务器接收到则马上返回一个值.如客户机发一个"1+1"的消息,服务器马上返回"2".当然里边要加一些你自己的约定格式串,不然客户机多了分不清.在指定的时间内没接收到返回值的话说明网络已经出现故障.  
   
       
     
  支持

如果你想快可以用   IDIcmpClient为判断,直接用Adoconnection有个响应时间,比较长了点  
   
  function   conip_only:   Boolean;    
  begin  
      result   :=   false;  
      IdIcmpClient1.Host:='192.168.0.2';  
      try  
              IdIcmpClient1.Ping;  
              result   :=   true;  
      except  
                on   e:exception   do  
                showmessage(e.Message+'网络通讯中断.');  
      end;  
  end;

procedure   TdtmConnect.tiKeepConnectTimer(Sender:   TObject);  
  begin  
      try  
          conMain.Execute('Select   @@CONNECTIONS');  
          //MsgBox('服务器正常连接中...');  
      except  
          tiKeepConnect.Enabled   :=   False;     //停止时钟走动  
          ErrBox('网络断开,与服务器的连接已经中断。');  
   
          if   not   Assigned(frmConnect)   then  
              frmConnect   :=   TfrmConnect.Create(self);  
          try  
              frmConnect.ShowModal;  
          finally  
              FreeAndNil(frmConnect);  
          end;  
      end;  
  end;

什么情况下使用呢?  
  通常,网络断开的话,系统会出错的,这时候就需要检查网络了

TAdoConnect组件有多个事件可用于判断

我将程序中所有用到查询、执行SQL、执行存储过程的地方封装成几个函数,然后在这几个函数执行失败的时候调用如下函数:  
   
  //再次联接  
  function   TDataModuleForm.ReConnect(Connection:   TADOConnection):boolean;  
  var   i,j:integer;   FBreak,FErr:boolean;  
  label   lbl_try;  
  begin  
      result:=false;  
      FBreak:=false;  
      FErr:=false;  
      for   i:=0   to   connection.Errors.Count-1   do  
      begin  
            if   FBreak   then   Break;  
            if   connection.Errors[i].Number=-2147467259   then  
            begin  
                  FBreak:=true;  
                  connection.Errors.Clear;  
                  if   messagedlg('系统检测到数据库联接已断开,需要系统尝试重新联接数据库吗?',mtinformation,[mbOk,mbCancel],0)=1   then  
                  begin  
  lbl_try:  
                      connection.Close;  
                      FErr:=false;  
                      j:=connection.ConnectionTimeout;  
                      connection.ConnectionTimeout:=5;  
                      screen.Cursor:=-11;  
                      application.ProcessMessages;  
                      try  
                              try  
                                      connection.Connected:=true;  
                              except  
                                      FErr:=True;  
                              end;  
                      finally  
                              connection.ConnectionTimeout:=j;  
                              screen.Cursor:=0;  
                              connection.Errors.Clear;  
                      end;  
                      if   FErr   and   (Messagedlg('联接失败,再次尝试联接吗?',mtinformation,[mbOk,mbCancel],0)=1)   then   goto   lbl_try;  
                  end;  
            end;  
      end;  
      result:=not   FErr;  
  end;

try  
       
  except  
      判断是否是断开连接的异常  
  end;

kaper()与zczb(zczb)的解答比较有建设性,谢谢。

嗨,这个真的没必要搞得那么复杂,楼主这样才比较耗资源啊

这样啦:还是用timer1  
   
  多放个   adodataset1,    
   
  在timer1.ontimer里写:  
  try  
      adodataset1.close;  
      adodataset1.commandtext:='select   getdate()';  
      adodataset1.open;  
  expect  
      showmessage('连接断开');  
  end;  
       
 

我不喜欢做自动连接。因为例如日志写满。或者其它原因。你这样的动作,会导致系统长时间处于检查数据库的连接中,但事实上却是暂时连不上来的,务必会导致死循环!   我想楼主的意在寻找能够检测到数据断开连接应该也是为了能够自动连接吧,退一步说,软件的使用出现连接不上数据库的时候。无非几个原因。1,网络   2,数据库服务器,3,数据库软件。4。程序本身。   5。其它       这些原因都会迫使你停下来调整。没有检测和重新连接的必要!   个人愚见,

楼主想复杂了.  
   
  try  
  ......  
  .....  
  .....  
  except  
      adoconnection.close;//   为下一次连接作准备.  
  end  
 

还是未搞懂我的意思。

我们这里的软件遇到这个问题,解决的办法是,每次用数据库的时候都把connection和query打开一次,同时在界面上留有panel来提示网络断开还是数据库断开(下面两个函数是我们用来判断网络断开还是数据库断开的函数):  
  function   CheckNetConnection(IP:   string):   Boolean;  
  var  
      Echo:   TIdEcho;  
  begin  
      Echo   :=   TIdEcho.Create(nil);  
      with   Echo   do  
      begin  
          try  
              Host   :=   IP;  
              Connect(1000);  
              Result   :=   True;  
          except  
              Result   :=   False;  
          end;  
      end;  
      Echo.Free;  
  end;  
   
  function   CheckDBConnection(IP:   string):   Boolean;  
  var  
      Telnet:   TIdTelnet;  
  begin  
      Telnet   :=   TIdTelnet.Create(nil);  
      try  
          with   Telnet   do  
          begin  
              if   Connected   then   Disconnect;  
              Host   :=   IP;  
              Port   :=   1433;//数据库服务用的端口号(Sql   server使用这个端口号)  
              try  
                  Connect(1000);  
                  Result   :=   True;  
              except     //否则重连  
                  Result   :=   False;  
              end;  
          end;  
      finally  
          Telnet.Disconnect;  
          Telnet.Free;  
      end;  
  end;

上面两个函数在Timer中周期性检查,我们的timer时间间隔为10秒,显示稍微之后,不过客户能够容忍,另外断开网络的时候检索数据库还是偶尔卡一点,不过无需重起软件,只要处理网络和数据库就行了

每次有人找我问起楼主那个经典问题,我都是同样的回答..  
  可几乎每次问问题的人总以为我在开玩笑.  
   
  当数据库连接断开时,这时候需要做什么呢?是试图重连?不断检查连接状态?    
  NO!   不要这样做!  
  数据库为什么断开?多半网络问题或者数据库当机或重启.事实上,这时候试图程序自动重连,  
  毫无意义!这样做反而会为负担很重的网络更没有恢复的机会.  
   
  这时候该做什么?   是的,释放连接对象,很多封装底层连接的组件,都会暂存连接状态,  
  当底层实际连接失败,封装对象的状态却是连接正常,这时候,连接不可能得到恢复.  
  怎么让封装对象与底层连接状态一致?   很简单,让连接对象CLOSE!  
   
  怎么知道连接失败?   在对数据库操作时!   对于VCL,组件会扔出数据库类型的异常,要做的,就是抓住它.对于使用数据感知控件,在Application.OnException事件中也可以得到它.  
  在处理这样异常时,就按刚才说的那样,"让连接对象CLOSE"!  
   
  对于状态为CLOSE的连接对象,不需要再操心什么.在使用VCL封装Ado时,当连接对象AdoConnection状态为关闭时,任何一个数据库的操作,它都会自动去连接.这是缺省的属性设计.  
   
 

同意楼上,也许我们考虑的楼上的意见以后可以解决我们软件仅有的一顿的问题,那样提示\重连\而且还不顿,就比较好了

TDataForm   =   class(TDataModule)  
          //...  
          procedure   DataModuleCreate(Sender:   TObject);  
      private  
          FOldApplicationOnException     :   TExceptionEvent;  
          procedure   HandleException(Sender:   TObject;   E:   Exception);  
      ...  
      end.  
   
  procedure   TDataForm.DataModuleCreate(Sender:   TObject);  
  begin  
      ...  
      FOldApplicationOnException     :=     Application.OnException;  
      Application.OnException   :=     HandleException;  
  end;  
   
  procedure   TDataForm.HandleException(Sender:   TObject;   E:   Exception);  
  begin  
      if   E.ClassNameIs('EOleException')   then  
          if   ShowMsg(Screen.ActiveForm,   format(  
                  '数据库连接被意外中止,要重新连接吗?'#10#13+  
                  '错误原因:%s',  
                  [E.Message]))   =   IDYES   then  
          begin  
              MainConn.Connected     :=     False;  
              MainConn.Connected     :=     True;  
              Exit;  
          end;  
      if   Assigned(FOldApplicationOnException)   then  
          FOldApplicationOnException(Sender,   E)  
  end;

mark

我也遇到了类似的问题,   大家有时间看看  
  http://community.csdn.net/Expert/topic/5460/5460676.xml?temp=7.497805E-02

posted on 2009-05-12 16:02 delphi2007 阅读(581) 评论(0)  编辑 收藏 引用
只有注册用户登录后才能发表评论。