DELPHI,Firedac,连接池



(*******************************************************************************

FireDac连接池--从何应祖--SQLADOPoolUnit.pas改编,从ado转到firedac

*******************************************************************************

池满的情况下 池子DAC连接 动态创建

系统默认池子中 一个小时以上未用的 TFDConnection 连接 系统自动释放

使用如下

先Uses SQLFirDACPoolUnit 单元

在程序初始化时(initialization)创建连接池类

DAConfig := TDAConfig.Create('YxDServer.ini');

DACPool := TDACPool.Create(32);

在程序关闭时(finalization)释放连接池类

DACPool.Free;

DAConfig.Free;

调用如下

try

FDQuery.Connecttion:= DACPool.GetCon(DAConfig);

FDQuery.Open;

finally

DACPool.PutCon(FDQuery.Connecttion);

end;

QQ:2405414352

2021-3

如有优化 请传一份 。谢谢!

*********************************************************************************

代码源自:作者:何应祖--SQLADOPoolUnit.pas

********************************************************************************)

unit SQLFirDACPoolUnit;

interface

uses

Windows, SqlExpr, SysUtils, Classes, ExtCtrls, DateUtils, IniFiles, uEncry,

Messages, Provider, FireDAC.Comp.Client, FireDAC.Phys.MSSQL,

FireDAC.Phys.ODBCBase, FireDAC.DApt,FireDAC.Moni.FlatFile,FireDAC.Stan.Intf,

FireDAC.Moni.Base,QLog;

type// 数据库类型

TDBType = (Access, SqlServer, Oracle);

//数据库配置 DAC

type

TDAConfig = class

private

//数据库配置

ConnectionName: string; //连接驱动名字

ProviderName: string; //通用驱动

DBServer: ansistring; //数据源 --数据库服务器IP

DataBase: ansistring; //数据库名字 //sql server连接时需要数据库名参数--数据库实例名称

OSAuthentication: Boolean; //是否是windows验证

UserName: ansistring; //数据库用户

PassWord: ansistring; //密码

AccessPassWord: string; //Access可能需要数据库密码

Port: integer; //数据库端口

BDEBUG:Boolean; //是否记录sql语句

DriverName: string; //驱动

HostName: string; //服务地址

//端口配置

TCPPort: Integer; //TCP端口

HttpPort: Integer; //http 端口

LoginSrvUser: string; //验证中间层服务登录用户

LoginSrvPassword: string; //验证登录模块密码

public

constructor Create(iniFile: string); overload;

destructor Destroy; override;

end;

type

TDACon = class

private

FConnObj: TFDConnection; //数据库连接对象

FMDFF:TFDMoniFlatFileClientLink; //SQL记录对象

FAStart: TDateTime; //最后一次活动时间

function GetUseFlag: Boolean;

procedure SetUseFlag(value: Boolean);

procedure FDMFFOutput(ASender: TFDMoniClientLinkBase; const AClassName,

AObjName, AMessage: string);

public

constructor Create(DAConfig: TDAConfig); overload;

destructor Destroy; override;

//当前对象是否被使用

property UseFlag: boolean read GetUseFlag write SetUseFlag;

property ConnObj: TFDConnection read FConnObj;

property AStart: TDateTime read FAStart write FAStart;

end;

type

TDACPool = class

procedure OnMyTimer(Sender: TObject); //做轮询用

private

FSection: TRTLCriticalSection;

FPoolNumber: Integer; //池大小

FPollingInterval: Integer; //轮询时间 以 分 为单位

FDACon: TDACon;

FList: TList; //用来管理连接

FTime: TTimer; //主要做轮询

procedure Enter;

procedure Leave;

function SameConfig(const Source: TDAConfig; Target: TDACon): Boolean;

function GetConnectionCount: Integer;

public

constructor Create(const MaxNumBer: Integer; FreeMinutes: Integer = 60;

TimerTime: Integer = 5000); overload;

destructor Destroy; override;

//从池中取出可用的连接。

function GetCon(const tmpConfig: TDAConfig): TFDConnection;

//把用完的连接放回连接池。

procedure PutCon(const DAConnection: TFDConnection);

//释放池中许久未用的连接,由定时器定期扫描执行

procedure FreeConnection;

//当前池中连接数.

property ConnectionCount: Integer read GetConnectionCount;

end;

var

DACPool: TDACPool;

DAConfig: TDAConfig;

PoolNum: Integer = 32;

implementation

{ TDAConfig }

constructor TDAConfig.Create(iniFile: string);

var

AINI: TIniFile;

begin

try

AINI := TIniFile.Create(iniFile);

DBServer := AINI.ReadString('DB', 'Server', '');

DataBase := AINI.ReadString('DB', 'DataBase', '');

DBServer := DeCode(AINI.ReadString('DB', 'Server', ''));

DataBase := DeCode(AINI.ReadString('DB', 'DataBase', ''));

UserName := DeCode(AINI.ReadString('DB', 'UserName', ''));

PassWord := DeCode(AINI.ReadString('DB', 'PassWord', ''));

PoolNum := AINI.ReadInteger('YxCisSvr', 'Pools', 32);

BDEBUG := AINI.ReadBool('YxCisSvr', 'SQLDEBUG', False);

finally

Freeandnil(AINI);

end;

end;

destructor TDAConfig.Destroy;

begin

inherited;

end;

{ tdacon }

procedure TDACon.FDMFFOutput(ASender: TFDMoniClientLinkBase;

const AClassName, AObjName, AMessage: string);

begin

PostLog(llDebug,AMessage);

end;

constructor TDACon.Create(DAConfig: TDAConfig);

var

str: string;

begin

str := 'DriverID=MSSQL;Server=' + DAConfig.DBServer + ';Database=' + DAConfig.DataBase

+ ';User_name=' + DAConfig.UserName + ';Password=' + DAConfig.PassWord +

';LoginTimeOut=3';

FConnObj := TFDConnection.Create(nil);

FMDFF := TFDMoniFlatFileClientLink.Create(nil);

with FConnObj,FMDFF do

begin

//ConnectionTimeout:=18000;

ConnectionString := str;

//解决执行sql过程断线,等待时间过程 ,加上之后,数据量过大写入会超时!屏蔽!

//Params.add('ResourceOptions.CmdExecTimeout=3');

//解决查询只返回50条数据问题

Params.add('FetchOptions.Mode=fmAll');

//解决!,&等字符插入数据库时丢失

Params.add('ResourceOptions.MacroCreate=False');

Params.add('ResourceOptions.MacroExpand=False');

//////////SQL日志设置/////////

Params.add('MonitorBy=FlatFile');

Params.add('ConnectionIntf.Tracing=True');

FileName := '';

EventKinds := [ekcmdExecute];

ShowTraces := False;

OnOutput := FDMFFOutput;

try

FileEncoding := ecANSI;

Except

raise Exception.Create('正在初始化SQL跟踪日志!请重新提交数据!');

end;

///////////////////////////

try

Connected := True;

Tracing := DAConfig.BDEBUG;

except

raise Exception.Create('数据库连接失败!请检查数据库配置或者网络链接!');

end;

end;

end;

destructor tdacon.Destroy;

begin

FAStart := 0;

if Assigned(FConnObj) then

begin

if FConnObj.Connected then

FConnObj.Close;

FreeAndnil(FConnObj);

FreeAndnil(FMDFF);

end;

inherited;

end;

procedure tdacon.SetUseFlag(value: Boolean);

begin

//False表示闲置,True表示在使用。

if not value then

FConnObj.Tag := 0

else

begin

if FConnObj.Tag = 0 then

FConnObj.Tag := 1; //设置为使用标识。

FAStart := now; //设置启用时间 。

end;

end;

function tdacon.GetUseFlag: Boolean;

begin

Result := (FConnObj.Tag > 0); //Tag=0表示闲置,Tag>0表示在使用。

end;

{ TDACPool }

constructor TDACPool.Create(const MaxNumBer: Integer; FreeMinutes: Integer = 60;

TimerTime: Integer = 5000);

begin

InitializeCriticalSection(FSection);

FPOOLNUMBER := MaxNumBer; //设置池大小

FPollingInterval := FreeMinutes; // 连接池中 FPollingInterval 以上没用的 自动回收连接池

FList := TList.Create;

FTime := TTimer.Create(nil);

FTime.Enabled := False;

FTime.Interval := TimerTime; //5秒检查一次

FTime.OnTimer := OnMyTimer;

FTime.Enabled := True;

end;

destructor TDACPool.Destroy;

var

i: integer;

begin

FTime.OnTimer := nil;

FTime.Free;

for i := FList.Count - 1 downto 0 do

begin

try

FDACon := TDAcon(FList.Items[i]);

if Assigned(FDACon) then

FreeAndNil(FDACon);

FList.Delete(i);

except

end;

end;

FList.Free;

DeleteCriticalSection(FSection);

inherited;

end;

procedure TDACPool.Enter;

begin

EnterCriticalSection(FSection);

//System.TMonitor.Enter(self);

end;

procedure TDACPool.Leave;

begin

LeaveCriticalSection(FSection);

// System.TMonitor.Exit(self);

end;

//根据字符串连接参数 取出当前连接池可以用的tdaconnection

function TDACPool.GetCon(const tmpConfig: TDAConfig): TFDConnection;

var

i: Integer;

IsResult: Boolean; //标识

CurOutTime: Integer;

begin

Result := nil;

IsResult := False;

CurOutTime := 0;

Enter;

try

for i := 0 to FList.Count - 1 do

begin

FDACon := TDACon(FList.Items[i]);

if not FDACon.UseFlag then //可用

if SameConfig(tmpConfig, FDACon) then //找到

begin

FDACon.UseFlag := True; //标记已经分配用了

Result := FDACon.ConnObj;

IsResult := True;

Break; //退出循环

end;

end; // end for

finally

Leave;

end;

if IsResult then

Exit;

//池未满 新建一个

Enter;

try

if FList.Count < FPOOLNUMBER then //池未满

begin

FDACon := tdacon.Create(tmpConfig);

FDACon.UseFlag := True;

Result := FDACon.ConnObj;

IsResult := True;

FList.Add(FDACon); //加入管理队列

end;

finally

Leave;

end;

if IsResult then

Exit;

//池满 等待 等候释放

while True do

begin

Enter;

try

for i := 0 to FList.Count - 1 do

begin

FDACon := tdacon(FList.Items[i]);

if SameConfig(tmpConfig, FDACon) then //找到

if not FDACon.UseFlag then //可用

begin

FDACon.UseFlag := True; //标记已经分配用了

Result := FDACon.ConnObj;

IsResult := True;

Break; //退出循环

end;

end; // end for

if IsResult then

Break; //找到退出

finally

Leave;

end;

//如果不存在这种字符串的池子 则 一直等到超时

if CurOutTime >= 5000 * 6 then //1分钟

begin

raise Exception.Create('连接超时!');

Break;

end;

Sleep(500); //0.5秒钟

CurOutTime := CurOutTime + 500; //超时设置成60秒

end; //end while

end;

procedure TDACPool.PutCon(const DAConnection: TFDConnection);

var

i: Integer;

begin

{

if not Assigned(DAConnection) then Exit;

try

Enter;

DAConnection.Tag := 0; //如此应该也可以 ,未测试...

finally

Leave;

end;

}

Enter; //并发控制

try

for i := FList.Count - 1 downto 0 do

begin

FDACon := tdacon(FList.Items[i]);

if FDACon.ConnObj = DAConnection then

begin

FDACon.UseFlag := False;

Break;

end;

end;

finally

Leave;

end;

end;

procedure TDACPool.FreeConnection;

var

i: Integer;

function MyMinutesBetween(const ANow, AThen: TDateTime): Integer;

begin

Result := Round(MinuteSpan(ANow, AThen));

end;

begin

Enter;

try

for i := FList.Count - 1 downto 0 do

begin

FDACon := tdacon(FList.Items[i]);

if MyMinutesBetween(Now, FDACon.AStart) >= FPollingInterval then //释放池子许久不用的DAC

begin

FreeAndNil(FDACon);

FList.Delete(i);

end;

end;

finally

Leave;

end;

end;

procedure TDACPool.OnMyTimer(Sender: TObject);

begin

FreeConnection;

end;

function TDACPool.SameConfig(const Source: TDAConfig; Target: TDACon): Boolean;

begin

//考虑到支持多数据库连接,需要本方法做如下等效连接判断.如果是单一数据库,可忽略本过程。

{ Result := False;

if not Assigned(Source) then Exit;

if not Assigned(Target) then Exit;

Result := SameStr(LowerCase(Source.ConnectionName),LowerCase(Target.ConnObj.Name));

Result := Result and SameStr(LowerCase(Source.DriverName),LowerCase(Target.ConnObj.Provider));

Result := Result and SameStr(LowerCase(Source.HostName),LowerCase(Target.ConnObj.Properties['Data Source'].Value));

Result := Result and SameStr(LowerCase(Source.DataBase),LowerCase(Target.ConnObj.Properties['Initial Catalog'].Value));

Result := Result and SameStr(LowerCase(Source.UserName),LowerCase(Target.ConnObj.Properties['User ID'].Value));

Result := Result and SameStr(LowerCase(Source.PassWord),LowerCase(Target.ConnObj.Properties['Password'].Value));

//Result := Result and (Source.OSAuthentication = Target.ConnObj.OSAuthentication);

}

end;

function TDACPool.GetConnectionCount: Integer;

begin

Result := FList.Count;

end;

//初始化时创建对象

initialization

DAConfig := TDAConfig.Create(ChangeFileExt(ParamStr(0), '.ini'));

DACPool := TDACPool.Create(PoolNum);

finalization

if Assigned(DACPool) then

DACPool.Free;

if Assigned(DAConfig) then

DAConfig.Free;

end.