程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 使用ADO,多線程訪問ACCESS數據庫的開發

使用ADO,多線程訪問ACCESS數據庫的開發

編輯:Delphi

  本人最近正在進行呼叫中心的座席端和服務器軟件開發,座席端登錄部分是進行提取主機信息,然後使用SOCKET提交給遠程服務器,服務器再在後台數據庫中進行查找信息,進行對比,看是否允許座席端主機登錄,因為我的電腦中無法安裝SQLSERVER,所以 ,後台數據庫暫時使用Access小型數據庫進行代替,到後期可以改為SQLSERVER數據庫,,我在後台數據庫中使用了兩個表,使用WORKMARK字段將其相關聯,如果在第一個表中查找到信息以後馬上會進入第二個表中進行查找,如果第一個表中的信息符合,就返回個客戶第二個表中的信息,因為座席端主機有200多台,為了防止幾台主機同時進行驗證信息,引起沖突,我在服務器端建立多線程,利用線程進行後台的查詢,以及查找結果的回傳,編寫成功後進行測試,發現當一個主機連接後可以測試成功,但是,當第二個或者以後的線程產生時,數據集在第一個表中可以查到正確的信息,但是進入第二個表中時,會發現按照正確的查找條件進行查找時,查到的信息都為NIL,而且經過跟蹤,發現,其實ADO已經找到了正確的信息,而且已經發送成功了,但是卻無法接收到,當時我在論壇中發表了一個帖子尋求幫助,原貼內容:http://expert.csdn.Net/Expert/topic/1420/1420051.XML?temp=.3060114

  有些朋友說可能Access數據庫不提供多線程訪問,但是我聽說很多的論壇中好像都使用了Access進行後台線程的查找的。翻看MSDN中關於COM的說明:發現我的線程中竟然缺少最重要的兩個函數過程

  CoInitialize( nil );凡是訪問com對象都應該使用。線程的關閉時應該使用CoUnInitialize;否則ADO肯定出錯,我相信肯定有不少的朋友經歷過這種問題的:

  我的線程代碼如下:

  unit Unitquerythread;

  interface

  uses
    Windows,SysUtils,Classes, Dialogs,ADODB,db, NMMSG, ActiveX{必須要有};

  type
    QueryThread = class(TThread)
    private
     FComputerInfor,FIPaddressinfo: string;  //傳遞的信息
     FAdoQuery,FAdoQUserInfor:TadoQuery;  //動態生成的表一,表2
    
     Fadoconnection:Tadoconnection;  //動態生成的連接
     Fnmmsg,FNMretureMSG:TNMMSG;//用來發送返回信息的組件
  filestream:tfilestream;
      procedure ExtractInfor(TransInfo:string{自定義的從該提交信息中分離信息的過程});
      procedure ShellexeQuery;
    Destructor Destroy;

    protected
      procedure Execute; override;
    public
  constructor  create(ComputerInfor:string;{傳輸來的計算機信息}IPaddressinfo:string{傳輸來的IP地址信息};nmmsg,NMretureMSG:TNMMSG);
    end;
  var
    ExtractDomainUser:string;  //提取出來的域用戶名
    ExtractComputername:string;//提取出來的計算機名稱
    ExtractDomainname:string;//提取出來的域名
    strlist:Tstringlist;
    IPlist:Tstringlist;//保存查找到的本工作組的ip地址信息
    const defeat='defeat';  //驗證失敗,必須注銷客戶機,在數據庫中重新進行寫入

  
  implementation

  
    uses Unitsendthread;

  
  constructor QueryThread.create(ComputerInfor, IPaddressinfo: string;nmmsg,NMretureMSG:TNMMSG);
  begin
    CoInitialize( nil );//使用com對象必須要初始化
   
inherited create(false);
    FreeOnTerminate:=true;
    FComputerInfor:=ComputerInfor;
    FIPaddressinfo:=IPaddressinfo;
    IPlist:=Tstringlist.create;//創造ip列表
    FAdoQuery:=TAdoQuery.Create(nil);     //動態生成表一
    FAdoQUserInfor:=TadoQuery.Create(nil);//動態生成表2
    Fadoconnection:=Tadoconnection.Create(nil);//動態生成的連接
    Fadoconnection.LoginPrompt :=false;
    Fadoconnection.KeepConnection :=true;
    Fadoquery.Connection:=Fadoconnection;
    FAdoQUserInfor.Connection :=Fadoconnection;
    Fadoconnection.ConnectionString :='Provider=Microsoft.Jet.OLEDB.4.0;Data Source=PersonInformation.mdb;Mode=Read;Persist Security Info=False';

  
    Fnmmsg:=nmmsg;
    FNMretureMSG:=NMretureMSG;
    strlist:=Tstringlist.Create;
   
  end;

  destructor QueryThread.Destroy;
  begin
    FAdoQuery.FREE;
    FAdoQUserInfor.FREE;
    Fadoconnection.Free;
    strlist.Free;
    iplist.free;
    CoUnInitialize;//必須使用
  
inherited destroy;
  end;

  procedure QueryThread.Execute;
  begin

  try
   begin
    extractinfor(FComputerInfor);  //進行信息提取
   Synchronize(ShellexeQuery);//
   end;
  except

  self.Terminate;

  end;

  end;

  procedure QueryThread.ExtractInfor(TransInfo: string);//改過程進行提取用#進行分割//的串裡的各個字符串
  
  integer;
  j:array [1..2]of integer;//用來保存分割符的位置
  
H,L,M,N:integer;//進行組合字符串時的循環參數

  begin
   
  全局變量,首先清空
        ExtractDomainUser:='';
        ExtractDomainname:='';
        ExtractComputername:='';
        H:=1;

  //=================================
   try
      begin
           for i := 1 to length(TransInfo) do

          if TransInfo[i]='#' then //找到了
             begin
              j[H]:=i;
              
              inc(H);
              continue;  //跳出循環
             end;

      BEGIN
       for L:=1 to j[1]-1 do
            ExtractDomainUser:=ExtractDomainUser+TransInfo[L]; //提取的登陸域用戶名
            ://showmessage(extractdomainuser);
       for M:=j[1]+1 to j[2]-1 do
            ExtractDomainname:=ExtractDomainname+TransInfo[M];//提取的域控制器名
          //showmessage(extractdomainname);
       for N:=j[2]+1 to length(trim(TransInfo)) do
            ExtractComputername:=ExtractComputername+Transinfo[N];//提取的計算機名
           //showmessage(extractcomputername);
      END;
     end;//進行異常處理
    Except
     // messagebox(0,'信息提取出現錯誤!','提示信息',mb_iconinformation);
    end;
  end;
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  procedure QueryThread.ShellexeQuery;
  var
      mark:string;   //保存查找到的工號信息
      SkillInfor:string;
      RetureInformation:string; //返回個客戶端的信息
      WorkGroupUserInfor:Tstringlist;
      filename:string;

  begin
   try
     begin
        
         FAdoQuery.Close;
         Fadoquery.SQL.Clear;
         FAdoQuery.SQL.Add ('SELECT * From LOGON WHERE DomainUser='+'"'+ExtractDomainUser+'"'+'and DomainName='+'"'+ExtractDomainname+'"'+'and ComputerName='+'"'+ExtractComputername+'"'+'and IPaddress='+'"'+FIPaddressinfo+'"');
         FAdoQuery.Prepared :=true;
         FAdoQuery.active:=true;
  file://=====================================================================
     if (FAdoQuery.fieldbyname('WorkMark').asstring ='') or (FAdoQuery.fIEldbyname('Privilege').asstring='')  then //沒有查到工號信息
           begin

               Fnmmsg.Disconnect;
               Fnmmsg.Host:=FIPaddressinfo;
               Fnmmsg.Port :=6711;
               Fnmmsg.FromName :='a';
               Fnmmsg.PostIt(defeat); //沒有找到權限和工號則發送錯誤消息
               FAdoQuery.active:=false;
           end

    else      //查詢到了工號,和權限信息 ,從階連表中得到所有信息
         begin

                strlist.Clear;
                strlist.Add(FAdoQuery.fIEldbyname('WorkMark').asstring);//把工號寫入
                mark:=FAdoQuery.fIEldbyname('WorkMark').asstring; //將工號寫入mark臨時變量

                FAdoQuery.close; //關閉第一個數據集
     //===========進入第二個表進行查找===============================================
      with FAdoQUserInfor do
         begin
              
                if active =true then close;   //關閉數據集
                sql.Clear;

                sql.Add('Select * From WORKER Where WorkMark='+'"'+mark+'"'); //該部分可能要求具體化內容
                Prepared :=true;
                open;
                //連接第二個線程時這裡出現問題

              if (fieldbyname('Phone').asstring='')or (fieldbyname('Name').asstring='')or (fIEldbyname('SkillLevel').asstring='')then
                 begin
               showmessage('nil');
                    Fnmmsg.Disconnect;
                    Fnmmsg.Host:= FIPaddressinfo;
                    Fnmmsg.Port :=6711;
                    Fnmmsg.FromName :='b';
                    Fnmmsg.PostIt(defeat);  //發送錯誤消息
                    close;
                 end

             else    //如果找到了電話號碼
                 begin

                     strlist.add(fIEldbyname('Name').asstring);  //寫入姓名
                     SkillInfor:=fIEldbyname('SkillGroup').asstring;
                     strlist.add(fieldbyname('SkillGroup').asstring+'業務處理='+fIEldbyname('SkillLevel').asstring); //上海市業務處理=1
                     strlist.Add(fIEldbyname('Phone').asstring);  //寫入電話號碼
                   
                     RetureInformation :=strlist.Strings[0]{工號}+'#'+strlist.Strings[1]{姓名}+'#'+strlist.Strings[2]+'#'{技能信息}+strlist.strings[3]{電話號碼};
                     //edit;                      //煩會給客戶端的信息
                    // fIEldbyname('IsLogon').asstring:='1'; //登錄成功,寫入成功標志
                    // Post;

                     FNMretureMSG.Disconnect;
                     FNMretureMSG.Host :=FIPaddressinfo;
                     FNMretureMSG.Port:=6722; //回傳信息遠程端口為6722;
                     FNMretureMSG.PostIt(RetureInformation);  //返回成功的信息

  
  /·······························
     {進行查詢工作組信息,然後傳送信息}
                     if active =true then close;
                     sql.clear;
                     SQL.Add('SELECT * FROM WORKER WHERE SkillGroup='+'"'+SkillInfor+'"');
                     open;
                  
                     WorkGroupUserInfor:=Tstringlist.create;

                   while not eof do
                    begin
                     WorkGroupUserInfor.Add(fIEldByName('Name').asstring ); {index0 name}
                     WorkGroupUserInfor.Add(fIEldByName('WorkMark').asstring ); {index1 workmark}
                     WorkGroupUserInfor.Add(fIEldByName('Phone').asstring );    {index2 Phone}
                     WorkGroupUserInfor.Add(fieldByName('SkillGroup').asstring +'業務處理='+fIEldbyname('SkillLevel').asstring);{index3 SkillGroup}
                     IPlist.Add(fIEldbyname('IPaddress').asstring); ://所有的ip地址保留到內存中
                     //showmessage(fIEldbyname('IPaddress').asstring);
                     iplist.SaveToFile (fIEldByName('SkillGroup').asstring+'IPlst.INI');
                     next;
                   end;
                   WorkGroupUserInfor.SaveToFile(fIEldByName('SkillGroup').asstring+'.ini');
                   close;
               end;
            end;
       end;
     end;
    except      //防止查詢失敗
      begin
        FAdoQuery.close;
        FAdoQUserInfor.close;
     end;
   end;

   end;

  end.
  注意:使用這兩個函數應該加上activex.pas單元,否則,無法運行。

  我個人認為,如果開發基於ADO的多線程操作,最好在每個線程中放置一個連接,而數據集和數據連接都使用動態生成,當然你必須要注意必須要保證線程能過正常的釋放,否則你的電腦很快就快死機了,呵呵,當然還有第二種方法,你可以生成一個數據模塊,靜態放置數據集和ADOCONNECTION組件,然後使該數據模塊不在程序開始時生成,而可以在線程中動態的生成,這種方法也可以,有興趣的朋友可以試一下二者之間的執行效率。

  而對於ADO,我認為在開始情況下,如果不初始函數和後來的釋放函數,卻不能報錯,應該是一個BUG吧。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved