ADOConnection连接SQLServer自动断网问题解决

tzdgg 2014-12-23 02:44:08
Win7上ADO连接SQLServer过几十分钟后自动断网(被防火墙拦截等)问题终于解决了,困惑了很久
今天终于解决了!方法很简单,和大家共享一下。

问题现象:ADO连接SQLServer过几十分钟后(有的过几周)数据库连接无缘无故断开,
再做数据库操作报错“连接失败”。实际上此时数据库服务器可以ping通,
新创建其他ADO控件连接数据库也没问题。就这个ADO不行了。

问题分析:
刚开始想得比较简单,只要创建个线程或者Timer时时判断ADOConnecton1.Active属性=false不得了么。但实际上因为后台原因
或者服务断开再重连、被防火墙拦截等意外情况发生时ADOConnecton1.Active属性仍然是true!无法判断。
后来想到用ping,如果ping不通那就断开了?!但是ping通了未必说明数据库就能连通!ping无法判断数据库能否连通。
那么线程里面不断执行个select GetDate 之类简单SQL,如果失败就判断数据库断开行不行呢?显然不行,
多用户同时不断连接数据库对服务器压力太大了,不可取。
后来网上查了很多材料,有人提出捕获OleException的方法,既不创建线程和定时器判断数据库是否断开,而是当用户执行操作
发生Ole异常时捕获它,如果是数据库连接错误,那么恢复数据库连接即可,我在他们代码基础上完善了一下,以下是实现代码。

控件:

Button1: TButton;
ADOConnection1: TADOConnection;
Button2: TButton;
ADOQuery1: TADOQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
ApplicationEvents1: TApplicationEvents;

代码:

uses ComObj;

{$R *.dfm}

procedure TForm1.ApplicationEvents1Exception(Sender: TObject; E: Exception);
var
I: integer;
begin
//请执行如下命令或者其他方法强制产生数据库连接断开情况,以触发如下异常。
//net stop MsSqlServer
//net start MsSqlServer
if (E is EOleException) and ((E as EOleException).ErrorCode= -2147467259) then
begin
ADOConnection1.Connected := False;
try
ADOConnection1.Connected := True;
except On E2: Exception do
begin
MessageDlg('重连数据库发生错误:'#13 + E2.Message, mtError, [mbOK], 0);
end;
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
sSQL: string;
begin
sSQL:= 'Provider=SQLOLEDB.1;Password=YourPassword;Persist Security Info=True;' +
'User ID=sa;Initial Catalog=YourDatabase;Data Source=.';

with ADOConnection1 do
begin
LoginPrompt:= false;
Connected:= false;
ConnectionString:= sSQL;
Connected:= true;
end;
ShowMessage('ok');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
with ADOQuery1 do
begin
Close;
SQL.Clear;
SQL.Add('select * from Test');
Open;
end;
end;

至于怎么避免数据库频繁断开问题还没有解决,我觉得可能SQLServer或者防火墙可以设置吧,
如果有人知道,请回复邮件:tzdgg@163.com万分感谢!
...全文
2501 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
海宏AA 2016-07-26
  • 打赏
  • 举报
回复
//网络是否还通 function getConnectionConnected(AConn:TAdoConnection):Boolean; var conn:TAdoConnection; err:Error; lNetErr:Boolean; begin conn:=AConn; result:=conn.connected; with conn.errors do try if not result then exit; //未连接 //网络是否断开 result:=not ( (Count>0) and ((Item[0].Number=-2147467259) or (pos('网络',Item[0].Description)>0) or (pos('连接', Item[0].Description)>0)) ); except end; end; //检查网络是否还通着,没通就自动重连 function checkConnectionConnected(AConn:TAdoConnection; lIfDisconnectTryReConnect:Boolean=true):Boolean; label lbl_End; var conn:TAdoConnection; err:Error; lConn, lRe:Boolean; begin conn:=AConn; lRe:=lIfDisconnectTryReConnect; with conn.errors do try result:=conn.connected; lConn:=getConnectionConnected(conn); //网络是否断开 if lConn then goto lbl_End; //网络正常 //处理网络已断开:自动重连 if lRe then try conn.close; conn.open; conn.Errors.Clear; //清空错误 except on x:Exception do ; //x.free end; lbl_End://处理完成 finally result:=conn.connected; end; end;
道玄希言 2014-12-25
  • 打赏
  • 举报
回复
如果只考虑断网的问题, 我们可以假定我们的查询等语句, 平时都是正确的, 一旦出错, 抛出异常时, 不管什么原因, 我们都重连网络一次. 如果网络连接正确, 则再次执行操作.
道玄希言 2014-12-25
  • 打赏
  • 举报
回复
我是直接在查询语句中判断的. procedure TForm1.Button2Click(Sender: TObject); begin try 查询,等操作 except 重连; if ( 如果连接成功) begin try 然后重新执行查询等操作 except 返回错误. end; end; end; end;
不得闲 2014-12-25
  • 打赏
  • 举报
回复
多谢共享,看看系统的安全设置是否有这种阻断,昨天我也是找了半天发现在系统的安全设置中没开放我的程序,结果外面的都无法访问我的电脑上的服务器程序
tzdgg 2014-12-25
  • 打赏
  • 举报
回复
今天和同事讨论了一下,实际上这种情况只要封装一下数据库操作的方法,每次执行select、insert、update、delete前执行个getDate之类简单的SQL,如果网没断那就瞬间正确返回日期,如果没有那就网断了,这个费点时间,就重连网络就行了,不过大部分情况是假断网的情况。SQLServer定期断开的问题普遍存在,所以说到底就是数据库访问没有进行良好封装导致,因为SQL语句太多,没封装的情况下要是每个地方都加个GetDate判断是否断网肯定累死。所以封装有多重要。
lyhoo163 2014-12-25
  • 打赏
  • 举报
回复
还可以采取心跳。定时少流量连接。
固执的大叔 2014-12-25
  • 打赏
  • 举报
回复
谢谢分享,共同进步!我的方法跟你的差不多!
lyhoo163 2014-12-23
  • 打赏
  • 举报
回复
谢谢共享。只有共享,才能共同提高。
tzdgg 2014-12-23
  • 打赏
  • 举报
回复
-2147467259错误代码是网上抄的,你测试一下就知道了,确实返回这个错误代码。 这个判断比较靠谱。
levonsoft 2014-12-23
  • 打赏
  • 举报
回复
if (E is EOleException) and ((E as EOleException).ErrorCode= -2147467259) then 这个靠谱吗? 这个errorcode 代表?? 我以前的做法就是定期访问数据库中的某个表的(其实就是执行一条最简单的sql语句) 如果失败,说明sql 连接异常。需要重连。 active 确实是不靠谱的。
一个使用ADO连接池的示例,演示了TADOStoredProc动态参数的使用,带重连机制 =================== unit UnitDemo; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm2 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2; //数据库服务器 gDBServer: String = '127.0.0.1'; //数据库名称 gDBName: String = 'master'; //数据库用户名 gDBUser: String = 'sa'; //密码 gDBPass: String = '2001'; implementation {$R *.dfm} uses ADODB, UnitADOConnectionPool; const CreateSQL = 'create procedure TestMyPool (@type sysname) '#13#10+ 'as'#13#10+ 'select * from sysobjects where xtype=@type'#13#10+ 'return @@rowcount'; DeleteSQL = 'if Exists(select 1 from sysobjects where xtype=N''P'' and name=N''TestMyPool'')'#13#10+ ' drop procedure TestMyPool'; var gPoolMan: TADOConnPoolMan = Nil; procedure TForm2.Button1Click(Sender: TObject); var ADOObject:TADOConnPoolObject; ADOStoredProc:TADOStoredProc; Running :Integer; I: Integer; begin //取得一个存储过程资源(含一数据库有效连接ADOObject := gPoolMan.CreateSP('TestMyPool'); if ADOObject = Nil then //取得资源失败 Exit; try ADOStoredProc := ADOObject.ExecObject as TADOStoredProc; Running := 2;//允许重试(两次)操作,以便在操作失败之后达到重连 while Running>0 do begin Dec(Running); if ADOObject.NeedRefresh then begin//判断是否有重连标志(比如数据库断开等,可能需要进行重连) if Not ADOObject.Reconnect then Exit; ADOObject.NeedRefresh := Not ADOStoredProc.Parameters.Refresh; if ADOObject.NeedRefresh then Exit; end; for I := 1(*Zero is the *Result* Parameter*) to ADOStoredProc.Parameters.Count - 1 do begin //========================= //传递参数 ADOStoredProc.Parameters.Items[I].Value := 'U'; //========================= end; if Running 0 then try //执行存储过程 ADOStoredProc.Open; //执行存储过程成功,退出循环进入后续的数据处理 break; except On E:Exception do begin //执行失败非程序级的异常通常有两种可能: //1.数据库连接断开 //2.自适合的参数传递当中可能存储过程已更新,参与不一致 //设置重连标志 ADOObject.NeedRefresh := True; //=================== //这里记录数据库操作失败日志 //=================== end; end; Exit; end; //========================== //从ADOStoredProc当中读取记录 ShowMessage(IntToStr(ADOStoredProc.Parameters.ParamByName('Result').Value)); //========================== //关闭存储对象的资源 ADOStoredProc.Close; finally //调用结束,释放资源 ADOObject.Free; end; end; procedure TForm2.FormCreate(Sender: TObject); var ADOConn:TADOConnection; begin (****************BEGIN*******************) (*注:仅为测试准备 *) //初始化测试环境 ADOConn := Nil; if Not TADOConnPoolMan.ConnectADO( gDBServer,gDBUser,gDBPass,gDBName,true,ADOConn) then Exit; try ADOConn.Execute(DeleteSQL); ADOConn.Execute(CreateSQL); finally try ADOConn.Close; except end; ADOConn.Free; end; (*****************END********************) //初始化连接池 gPoolMan := TADOConnPoolMan.Create(gDBServer,gDBUser,gDBPass,gDBName,true); end; procedure TForm2.FormDestroy(Sender: TObject); var ADOConn:TADOConnection; begin //释放连接池 if Assigned(gPoolMan) then gPoolMan.Free; (****************BEGIN*******************) (*注:仅为测试准备 *) //清理测试环境 ADOConn := Nil; if Not TADOConnPoolMan.ConnectADO( gDBServer,gDBUser,gDBPass,gDBName,true,ADOConn) then Exit; try ADOConn.Execute(DeleteSQL); finally try ADOConn.Close; except end; ADOConn.Free; end; (*****************END********************) end; end.

2,507

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 数据库相关
社区管理员
  • 数据库相关社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧