最爱巴菲特 發表於 2021-7-26 14:39:00

DELPHI的FireDac连接池单元

<p>&nbsp;</p>
<p><br>(*******************************************************************************<br>                            FireDac连接池<br>*******************************************************************************<br>池满的情况下 池子DAC连接 动态创建<br>系统默认池子中 一个小时以上未用的 TFDConnection 连接 系统自动释放<br>使用如下<br>先Uses SQLFirDACPoolUnit 单元<br>在程序初始化时(initialization)创建连接池类<br>DAConfig := TDAConfig.Create('YxDServer.ini');<br>DACPool := TDACPool.Create(32);<br>在程序关闭时(finalization)释放连接池类<br>DACPool.Free;<br>DAConfig.Free;<br>调用如下<br>try<br>FDQuery.Connecttion:= DACPool.GetCon(DAConfig);<br>FDQuery.Open;<br>finally<br>DACPool.PutCon(FDQuery.Connecttion);<br>end;<br>QQ:2405414352<br>2021-3<br>如有优化 请传一份 。谢谢!<br>*********************************************************************************<br>代码源自:作者:何应祖--SQLADOPoolUnit.pas<br>********************************************************************************)</p>
<p>unit SQLFirDACPoolUnit;</p>
<p>interface</p>
<p>uses<br>Windows, SqlExpr, SysUtils, Classes, ExtCtrls, DateUtils, IniFiles, uEncry,<br>Messages, Provider, FireDAC.Comp.Client, FireDAC.Phys.MSSQL,<br>FireDAC.DApt,FireDAC.Moni.FlatFile,FireDAC.Stan.Intf,<br>FireDAC.Moni.Base,QLog;</p>
<p>type// 数据库类型<br>TDBType = (Access, SqlServer, Oracle);<br>//数据库配置 DAC</p>
<p>type<br>TDAConfig = class<br>private<br>//数据库配置<br>    ConnectionName: string; //连接驱动名字<br>    ProviderName: string; //通用驱动<br>    DBServer: ansistring; //数据源 --数据库服务器IP<br>    DataBase: ansistring; //数据库名字 //sql server连接时需要数据库名参数--数据库实例名称<br>    OSAuthentication: Boolean; //是否是windows验证<br>    UserName: ansistring; //数据库用户<br>    PassWord: ansistring; //密码<br>    AccessPassWord: string; //Access可能需要数据库密码<br>    Port: integer; //数据库端口<br>    DriverName: string; //驱动<br>    HostName: string; //服务地址<br>//端口配置<br>    TCPPort: Integer; //TCP端口<br>    HttpPort: Integer; //http 端口<br>    LoginSrvUser: string; //验证中间层服务登录用户<br>    LoginSrvPassword: string; //验证登录模块密码<br>public<br>    constructor Create(iniFile: string); overload;<br>    destructor Destroy; override;<br>end;</p>
<p>type<br>TDACon = class<br>private<br>    FConnObj: TFDConnection; //数据库连接对象<br>    FAStart: TDateTime; //最后一次活动时间<br>    function GetUseFlag: Boolean;<br>    procedure SetUseFlag(value: Boolean);<br>    procedure FDMFFOutput(ASender: TFDMoniClientLinkBase; const AClassName,<br>      AObjName, AMessage: string);<br>public<br>    constructor Create(DAConfig: TDAConfig); overload;<br>    destructor Destroy; override;<br>//当前对象是否被使用<br>    property UseFlag: boolean read GetUseFlag write SetUseFlag;<br>    property ConnObj: TFDConnection read FConnObj;<br>    property AStart: TDateTime read FAStart write FAStart;<br>end;</p>
<p>type<br>TDACPool = class<br>    procedure OnMyTimer(Sender: TObject); //做轮询用<br>private<br>    //FSection: TRTLCriticalSection;<br>    FPoolNumber: Integer; //池大小<br>    FPollingInterval: Integer; //轮询时间 以 分 为单位<br>    FDACon: TDACon;<br>    FList: TList; //用来管理连接<br>    FTime: TTimer; //主要做轮询<br>    procedure Enter;<br>    procedure Leave;<br>    function SameConfig(const Source: TDAConfig; Target: TDACon): Boolean;<br>    function GetConnectionCount: Integer;<br>public<br>    constructor Create(const MaxNumBer: Integer; FreeMinutes: Integer = 60;<br>      TimerTime: Integer = 5000); overload;<br>    destructor Destroy; override;<br>//从池中取出可用的连接。<br>    function GetCon(const tmpConfig: TDAConfig): TFDConnection;<br>//把用完的连接放回连接池。<br>    procedure PutCon(const DAConnection: TFDConnection);<br>//释放池中许久未用的连接,由定时器定期扫描执行<br>    procedure FreeConnection;<br>//当前池中连接数.<br>    property ConnectionCount: Integer read GetConnectionCount;<br>end;</p>
<p>var<br>DACPool: TDACPool;<br>DAConfig: TDAConfig;<br>PoolNum: Integer = 32;</p>
<p>implementation<br>{ TDAConfig }</p>
<p>constructor TDAConfig.Create(iniFile: string);<br>var<br>AINI: TIniFile;<br>begin<br>try<br>    AINI := TIniFile.Create(iniFile);<br>    DBServer := AINI.ReadString('DB', 'Server', '');<br>    DataBase := AINI.ReadString('DB', 'DataBase', '');<br>    DBServer := DeCode(AINI.ReadString('DB', 'Server', ''));<br>    DataBase := DeCode(AINI.ReadString('DB', 'DataBase', ''));<br>    UserName := DeCode(AINI.ReadString('DB', 'UserName', ''));<br>    PassWord := DeCode(AINI.ReadString('DB', 'PassWord', ''));<br>    PoolNum := AINI.ReadInteger('YxCisSvr', 'Pools', 32);<br>finally<br>    Freeandnil(AINI);<br>end;</p>
<p>end;</p>
<p>destructor TDAConfig.Destroy;<br>begin<br>inherited;<br>end;<br>{ tdacon }</p>
<p>procedure TDACon.FDMFFOutput(ASender: TFDMoniClientLinkBase;<br>const AClassName, AObjName, AMessage: string);<br>begin<br>PostLog(llDebug,AMessage);<br>end;</p>
<p>constructor TDACon.Create(DAConfig: TDAConfig);<br>var<br>str: string;<br>begin<br>str := 'DriverID=MSSQL;Server=' + DAConfig.DBServer + ';Database=' + DAConfig.DataBase<br>    + ';User_name=' + DAConfig.UserName + ';Password=' + DAConfig.PassWord;<br>FConnObj := TFDConnection.Create(nil);<br>with FConnObj do<br>begin<br>    //ConnectionTimeout:=18000;<br>    ConnectionString := str;<br>    //解决执行sql过程断线,等待时间过程 ,加上之后,数据量过大写入会超时!屏蔽!<br>    //Params.add('ResourceOptions.CmdExecTimeout=3');<br>    //解决查询只返回50条数据问题<br>    Params.add('FetchOptions.Mode=fmAll');<br>    //解决!,&amp;等字符插入数据库时丢失<br>    Params.add('ResourceOptions.MacroCreate=False');<br>    Params.add('ResourceOptions.MacroExpand=False');<br>    //////////SQL日志设置/////////<br>    //Params.add('MonitorBy=FlatFile');<br>    //Params.add('ConnectionIntf.Tracing=True');<br>    //FileName := '';<br>    //EventKinds := ;<br>    //ShowTraces := False;<br>    //OnOutput := FDMFFOutput;<br>    //try<br>    //FileEncoding := ecANSI;<br>    //Except<br>    //raise Exception.Create('正在初始化SQL跟踪日志!请重新提交数据!');<br>    //end;<br>    ///////////////////////////<br>    try<br>      Connected := True;<br>      //Tracing := DAConfig.BDEBUG;<br>    except<br>      raise Exception.Create('数据库连接失败!请检查数据库配置或者网络链接!');<br>    end;<br>end;<br>end;</p>
<p>destructor tdacon.Destroy;<br>begin<br>FAStart := 0;<br>if Assigned(FConnObj) then<br>begin<br>    if FConnObj.Connected then<br>      FConnObj.Close;<br>    FreeAndnil(FConnObj);<br>end;<br>inherited;<br>end;</p>
<p>procedure tdacon.SetUseFlag(value: Boolean);<br>begin<br>//False表示闲置,True表示在使用。<br>if not value then<br>    FConnObj.Tag := 0<br>else<br>begin<br>    if FConnObj.Tag = 0 then<br>      FConnObj.Tag := 1; //设置为使用标识。<br>    FAStart := now; //设置启用时间 。<br>end;<br>end;</p>
<p>function tdacon.GetUseFlag: Boolean;<br>begin<br>Result := (FConnObj.Tag &gt; 0); //Tag=0表示闲置,Tag&gt;0表示在使用。<br>end;<br>{ TDACPool }</p>
<p>constructor TDACPool.Create(const MaxNumBer: Integer; FreeMinutes: Integer = 60;<br>TimerTime: Integer = 5000);<br>begin<br>//InitializeCriticalSection(FSection);<br>FPOOLNUMBER := MaxNumBer; //设置池大小<br>FPollingInterval := FreeMinutes; // 连接池中 FPollingInterval 以上没用的 自动回收连接池<br>FList := TList.Create;<br>FTime := TTimer.Create(nil);<br>FTime.Enabled := False;<br>FTime.Interval := TimerTime; //5秒检查一次<br>FTime.OnTimer := OnMyTimer;<br>FTime.Enabled := True;<br>end;</p>
<p>destructor TDACPool.Destroy;<br>var<br>i: integer;<br>begin<br>FTime.OnTimer := nil;<br>FTime.Free;<br>for i := FList.Count - 1 downto 0 do<br>begin<br>    try<br>      FDACon := TDAcon(FList.Items);<br>      if Assigned(FDACon) then<br>      FreeAndNil(FDACon);<br>      FList.Delete(i);<br>    except<br>    end;<br>end;<br>FList.Free;<br>//DeleteCriticalSection(FSection);<br>inherited;<br>end;</p>
<p>procedure TDACPool.Enter;<br>begin<br>//EnterCriticalSection(FSection);<br>if Assigned(Self) then<br>    MonitorEnter(Self);<br>end;</p>
<p>procedure TDACPool.Leave;<br>begin<br>//LeaveCriticalSection(FSection);<br>if Assigned(Self) then<br>    MonitorExit(Self);<br>end;</p>
<p>//根据字符串连接参数 取出当前连接池可以用的tdaconnection</p>
<p>function TDACPool.GetCon(const tmpConfig: TDAConfig): TFDConnection;<br>var<br>i: Integer;<br>IsResult: Boolean; //标识<br>CurOutTime: Integer;<br>begin<br>Result := nil;<br>IsResult := False;<br>CurOutTime := 0;<br>Enter;<br>try<br>    for i := 0 to FList.Count - 1 do<br>    begin<br>      FDACon := TDACon(FList.Items);<br>      if not FDACon.UseFlag then //可用<br>      //if SameConfig(tmpConfig, FDACon) then //找到<br>      begin<br>      FDACon.UseFlag := True; //标记已经分配用了<br>      Result := FDACon.ConnObj;<br>      IsResult := True;<br>      Break; //退出循环<br>      end;<br>    end; // end for<br>finally<br>    Leave;<br>end;<br>if IsResult then<br>    Exit;<br>//池未满 新建一个<br>Enter;<br>try<br>    if FList.Count &lt; FPOOLNUMBER then //池未满<br>    begin<br>      FDACon := tdacon.Create(tmpConfig);<br>      FDACon.UseFlag := True;<br>      Result := FDACon.ConnObj;<br>      IsResult := True;<br>      FList.Add(FDACon); //加入管理队列<br>    end;<br>finally<br>    Leave;<br>end;<br>if IsResult then<br>    Exit;<br>//池满 等待 等候释放<br>while True do<br>begin<br>    Enter;<br>    try<br>      for i := 0 to FList.Count - 1 do<br>      begin<br>      FDACon := tdacon(FList.Items);<br>      //if SameConfig(tmpConfig, FDACon) then //找到<br>      if not FDACon.UseFlag then //可用<br>      begin<br>          FDACon.UseFlag := True; //标记已经分配用了<br>          Result := FDACon.ConnObj;<br>          IsResult := True;<br>          Break; //退出循环<br>      end;<br>      end; // end for<br>      if IsResult then<br>      Break; //找到退出<br>    finally<br>      Leave;<br>    end;<br>    //如果不存在这种字符串的池子 则 一直等到超时<br>    if CurOutTime &gt;= 5000 * 6 then //30s<br>    begin<br>      raise Exception.Create('未找到可用的连接!连接超时!');<br>      Break;<br>    end;<br>    Sleep(500); //0.5秒钟<br>    CurOutTime := CurOutTime + 500; //超时设置成30秒<br>end; //end while<br>end;</p>
<p>procedure TDACPool.PutCon(const DAConnection: TFDConnection);<br>var<br>i: Integer;<br>begin<br>{<br>if not Assigned(DAConnection) then Exit;<br>try<br>Enter;<br>DAConnection.Tag := 0; //如此应该也可以 ,未测试...<br>finally<br>Leave;<br>end;<br>}<br>Enter; //并发控制<br>try<br>    for i := FList.Count - 1 downto 0 do<br>    begin<br>      FDACon := tdacon(FList.Items);<br>      if FDACon.ConnObj = DAConnection then<br>      begin<br>      FDACon.UseFlag := False;<br>      Break;<br>      end;<br>    end;<br>finally<br>    Leave;<br>end;<br>end;</p>
<p>procedure TDACPool.FreeConnection;<br>var<br>i: Integer;</p>
<p>function MyMinutesBetween(const ANow, AThen: TDateTime): Integer;<br>begin<br>    Result := Round(MinuteSpan(ANow, AThen));<br>end;</p>
<p>begin<br>Enter;<br>try<br>    for i := FList.Count - 1 downto 0 do<br>    begin<br>      FDACon := tdacon(FList.Items);<br>      if MyMinutesBetween(Now, FDACon.AStart) &gt;= FPollingInterval then //释放池子许久不用的DAC<br>      begin<br>      FreeAndNil(FDACon);<br>      FList.Delete(i);<br>      end;<br>    end;<br>finally<br>    Leave;<br>end;<br>end;</p>
<p>procedure TDACPool.OnMyTimer(Sender: TObject);<br>begin<br>FreeConnection;<br>end;</p>
<p>function TDACPool.SameConfig(const Source: TDAConfig; Target: TDACon): Boolean;<br>begin<br>//考虑到支持多数据库连接,需要本方法做如下等效连接判断.如果是单一数据库,可忽略本过程。<br>{ Result := False;<br>if not Assigned(Source) then Exit;<br>if not Assigned(Target) then Exit;<br>Result := SameStr(LowerCase(Source.ConnectionName),LowerCase(Target.ConnObj.Name));<br>Result := Result and SameStr(LowerCase(Source.DriverName),LowerCase(Target.ConnObj.Provider));<br>Result := Result and SameStr(LowerCase(Source.HostName),LowerCase(Target.ConnObj.Properties['Data Source'].Value));<br>Result := Result and SameStr(LowerCase(Source.DataBase),LowerCase(Target.ConnObj.Properties['Initial Catalog'].Value));<br>Result := Result and SameStr(LowerCase(Source.UserName),LowerCase(Target.ConnObj.Properties['User ID'].Value));<br>Result := Result and SameStr(LowerCase(Source.PassWord),LowerCase(Target.ConnObj.Properties['Password'].Value));<br>//Result := Result and (Source.OSAuthentication = Target.ConnObj.OSAuthentication);<br>}<br>end;</p>
<p>function TDACPool.GetConnectionCount: Integer;<br>begin<br>Result := FList.Count;<br>end;<br>//初始化时创建对象</p>
<p>initialization<br>DAConfig := TDAConfig.Create(ChangeFileExt(ParamStr(0), '.ini'));<br>DACPool := TDACPool.Create(PoolNum);</p>
<p>finalization<br>if Assigned(DACPool) then<br>    DACPool.Free;<br>if Assigned(DAConfig) then<br>    DAConfig.Free;</p>
<p>end.</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/Yang-YaChao/p/15061341.html
頁: [1]
查看完整版本: DELPHI的FireDac连接池单元