上篇提到了在XE~XE6下安装UniDac。这篇,就基于UniDac,实现一个简单的数据库连接池。

文本的目录:

1、简单描述连接池实现的好处和原理;

2、连接池实现代码;

3、给出使用连接池的Demo(窗体文件代码 和 实现Pas代码);

本文所有的代码在XE环境上测试通过。如果要运行在XE以下版本,Demo请根据实现情况作修改。

1、简单描述连接池实现的好处和原理

现在开始介绍第1点,使用Delphi开发数据库应用软件,那是一把利器。当然,Delphi也能开发其它类型的产品,比如游戏之类,盛大的传奇就是用Delphi开发的;当然今天的话是数据库应用。很多的ERP,我了解的金蝶ERP和用友ERP就是用Delphi开发的,当然他们也有Web版本。MIS系统初期时基于单机版本,现在很多财务软件就有单机版本,后来发展成C/S架构,就是客户端-服务端架构,客户端提供UI界面,服务端实现业务逻辑;在后来就发展到多层结构,一直到N层,实现分布式结构。其实不管是单机结构,还是C/S结构,还是发展到目前的三层及多层结构,本身并对业务逻辑的编写,并没有多大差别。资料的CURD(C=Create,U=Update,R=Read ,D=Delete)操作都是一样。这就涉及到一个问题。在连接数据库,包括ODBC,ADO,ADO.net 还是 DBExpress,还是第三方的连接驱动,都是程序和数据库的连接通道,本文的UniDac也是一个通道。我们知道每一次数据库连接,都是需要消耗资源,包括TCP/IP连接,SQL缓存等开销。现在的问题,如果有一个 Pool,能把每次申请的SQLConnetion用完后,再放回池里,不释放,以备下次使用,那样不是节省了开销,又增加了效率,让连接访问数据库为更快速,特别是多线程下,对数据库的访问。那么实现原理是什么呢?可以设计简单或设计复杂,这要视实际情况而定。一般的思路,池对外提供一个接口,供程序调用。如果没有SQL连接,池自己生产一个,返回SQL连接对象;程序调用完,池就回收,不实际释放,等待下次调用。这里有个问题,就是控制池的最大连接数问题,不过对于一般的应用,这个问题可以先不用考虑。下面是访问时序图:

2、连接池实现代码:

{ Author:
  Purpose:   数据库连接池单元
  History:
  Modify
  desc: 本连接池针对MySQL数据库,根据实际情况,可以配置MSSQL,Oracle,DB2,SQLite等,当然具体中,要稍作修改
}

unit SqlConPool;

interface

uses
  SysUtils, Windows, Classes, IniFiles, Uni,
  MySQlUniProvider, MemDS;
// const
// AESKey = '3ABE2C927E89407D95AF-B4DCB0AD76FEF8F45194167A465F94C29E2ABB6E67C2';

type

TSQLConntionRecord = record
    HostName: string;
    Port: Integer;
    UserName: string;
    DBName: string;
    MyDataBase: string;
    Password: string;
  end;

TSQLConnectionPool = class
  private
    FDbType: string;
    FConList: TThreadList;
    function TestConnection(con: TUniConnection): boolean;
    function GetConnection: TUniConnection;
    function GetConnectionRecord: TSQLConntionRecord;
  public
    function Pop: TUniConnection;
    procedure Push(con: TUniConnection);
    constructor CreatePool;
    destructor Destroy; override;
    function GetDbType: string;
    function PoolCount: Integer;
  end;

TQryPool = class
  private
    function GetQry: TUniQuery;
    procedure con(qry: TUniQuery);
    procedure discon(qry: TUniQuery);
  public
    function Pop: TUniQuery;
    procedure Push(qry: TUniQuery);
  end;

var
  SQLConnectionPools: TSQLConnectionPool;
  QryPools: TQryPool;

implementation

{ TSQLConnectionPool }

constructor TSQLConnectionPool.CreatePool;
begin
  FConList := TThreadList.Create;
  FDbType := 'MYSQL';
end;

destructor TSQLConnectionPool.Destroy;
var
  i: Integer;
begin
  with FConList.LockList do
    try
      for i := Count - 1 downto 0 do
      begin
        TUniConnection(Items[i]).Close;
        TUniConnection(Items[i]).Free;
      end;
    finally
      FConList.UnlockList;
    end;

FConList.Free;
end;

//获取SQL连接对象
function TSQLConnectionPool.GetConnection: TUniConnection;
var
  con: TUniConnection;
  RecCon: TSQLConntionRecord;
begin
  Result := nil;
  try
    con := TUniConnection.Create(nil);
    RecCon := GetConnectionRecord;
    try

with con do
      begin
        LoginPrompt := false;
        ProviderName := RecCon.MyDataBase;
        UserName := RecCon.UserName;
        Password := RecCon.Password;
        Server := RecCon.HostName;
        Database := RecCon.DBName;
        Port := RecCon.Port;
        // 解决中文乱码,UniCode编码
        SpecificOptions.Values['UseUnicode'] := 'True';
        Connect;
      end;
      Result := con;
    except
      on E: exception do
      begin
        Result := nil;
        con.Free;
        // 打印日志。。。。
      end;
    end;
  except
  end;
end;

//获取配置SQL连接参数
function TSQLConnectionPool.GetConnectionRecord: TSQLConntionRecord;
var
  dbIni: TIniFile;
begin
  dbIni := TIniFile.Create(ExpandFileName(ExtractFilePath(ParamStr(0)) +
    '\DataBase.ini'));
  try
    with Result do
    begin
      HostName := dbIni.ReadString('Database', 'Host', '');
      Port := dbIni.ReadInteger('Database', 'Port', 3306);
      UserName := dbIni.ReadString('Database', 'UID', '');
      DBName := dbIni.ReadString('Database', 'Database', '');
      MyDataBase := UpperCase(dbIni.ReadString('Database', 'DataBaseType',
        'MySql'));
      Password := dbIni.ReadString('Database', 'Password', '');
      // 如果要加密处理,就通过DES或AES加密
      // Password := string(AesDecryptString(dbIni.ReadString('Database',
      // 'Password', ''), AESKey));
    end;
  finally
    dbIni.Free;
  end;
end;

//获取数据库类型,UniDac支持多种数据库类型,可以通过配置文件配置
function TSQLConnectionPool.GetDbType: string;
begin
  Result := FDbType;
end;

//获取连接池SQL对象个数
function TSQLConnectionPool.PoolCount: Integer;
begin
  with FConList.LockList do
    try
      Result := Count;
    finally
      FConList.UnlockList;
    end;

end;

//弹出SQL连接对象
function TSQLConnectionPool.Pop: TUniConnection;
begin
  with FConList.LockList do
    try
      if Count > 0 then
      begin
        Result := TUniConnection(Items[0]);
        Delete(0);
        if not TestConnection(Result) then
        begin
          Result.Free;
          Result := Pop;
        end;
      end
      else
      begin
        Result := GetConnection;
      end
    finally
      FConList.UnlockList;
    end;
end;

//回收SQL连接对象
procedure TSQLConnectionPool.Push(con: TUniConnection);
begin
  if con <> nil then
    with FConList.LockList do
      try
        Insert(0, con);
      finally
        FConList.UnlockList;
      end;
end;

//测试连接池中的SQL对象是否存活
function TSQLConnectionPool.TestConnection(con: TUniConnection): boolean;
begin
  Result := false;
  try
    con.ExecSQL('delete from dbcon where 1<>1', []);

Result := true;
  except
    on E: exception do
    begin
      // 实际应用,一定要打印日志
    end;

end;
end;

{ TQryPool }

//qry 关联SQL Connection
procedure TQryPool.con(qry: TUniQuery);
var
  sqlcon: TUniConnection;
begin
  sqlcon := SQLConnectionPools.Pop;
  qry.Connection := sqlcon;
end;

//回收SQL Connetion 对象
procedure TQryPool.discon(qry: TUniQuery);
begin
  SQLConnectionPools.Push(qry.Connection);
end;

//获取对象
function TQryPool.GetQry: TUniQuery;
var
  qry: TUniQuery;
begin
  qry := TUniQuery.Create(nil);
  con(qry);
  Result := qry;
end;

//弹出Qry对象
function TQryPool.Pop: TUniQuery;
begin
  Result := GetQry;
end;

//获取Qry对象
procedure TQryPool.Push(qry: TUniQuery);
begin
  if qry <> nil then
  begin
    qry.Close;
    discon(qry);
    qry.Free;
  end;
end;

initialization

SQLConnectionPools := TSQLConnectionPool.CreatePool();
QryPools := TQryPool.Create;

finalization

if QryPools <> nil then
begin
  QryPools.Free;
  QryPools := nil;
end;
if SQLConnectionPools <> nil then
begin
  SQLConnectionPools.Free;
  SQLConnectionPools := nil;
end;

end.

3、给出使用连接池的Demo;

窗体代码:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 310
  ClientWidth = 682
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 24
    Top = 8
    Width = 138
    Height = 25
    Caption = #20027#32447#27979#35797
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 24
    Top = 55
    Width = 138
    Height = 25
    Caption = #22810#32447#31243#27979#35797
    TabOrder = 1
    OnClick = Button2Click
  end
  object Memo1: TMemo
    Left = 184
    Top = 8
    Width = 490
    Height = 294
    Lines.Strings = (
      'Memo1')
    TabOrder = 2
  end
  object Button3: TButton
    Left = 24
    Top = 96
    Width = 138
    Height = 25
    Caption = #33719#21462#27744'SQL'#36830#25509#23545#35937#20010#25968
    TabOrder = 3
    OnClick = Button3Click
  end
end

实现代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,  StdCtrls;

const
  WM_PUSHDATA=WM_USER+100;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    procedure GetDtaTest;
    { Private declarations }
    procedure WMHandlePUSHDATA(var msg:TMessage);message WM_PUSHDATA;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses sqlConPool,uni;

{$R *.dfm}

//开启多个线程测试
procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to 50 do
  begin
    TThread.CreateAnonymousThread(GetDtaTest).Start;
  end;
end;

//显示当前连接池中SQLConnetion对象
procedure TForm1.Button3Click(Sender: TObject);
begin
   ShowMessage(Format('PoolCount=%d',[SQLConnectionPools.PoolCount]));
end;

//通过获取SQL对象,获取数据
procedure TForm1.GetDtaTest();
var
  qry: TUniQuery;
  uid: integer;
  susername, spw: string;
  str:String;
begin
  // 获取对象
  qry := QryPools.Pop;
  try
    with qry do
    begin
      SQL.Text := 'select * from user';
      Open;
      while not eof do
      begin
        uid := FieldByName('id').AsInteger;
        susername := FieldByName('username').AsString;
        spw := FieldByName('password').AsString;

str:=  Format('id=%d ,username=%s,password=%s',[uid,susername,spw]);

//因为如果在工作线程中,避免在主线程下操作UI;
         SendTextMessage(self.Handle,WM_PUSHDATA,0,str);

Next;
      end;
    end;
  finally
    // 回收对象
    QryPools.Push(qry);
  end;
end;

//打印显示获取数据
procedure TForm1.WMHandlePUSHDATA(var msg: TMessage);
var
  str:string;
begin
   str:=String(  msg.LParam );
   Memo1.Lines.Add(str) ;
end;

//主线程下测试
procedure TForm1.Button1Click(Sender: TObject);
begin
  GetDtaTest();
end;

end.

转载于:https://www.cnblogs.com/oldsheep/p/3788049.html

基于UniDac的数据库连接池相关推荐

  1. 基于DBUtils实现数据库连接池

    小知识: 1.子类继承父类的三种方式 class Dog(Animal): #子类 派生类def __init__(self,name,breed, life_value,aggr):# Animal ...

  2. 基于JDBC的数据库连接池技术研究与应用

    引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的3层开 ...

  3. JDBC工具类,基于C3P0的数据库连接池,提供获取连接池、获取连接对象、释放资源和封装事务操作的方法...

    /**  *  * JDBC工具类,基于C3P0数据库连接池的实现  *  * @author 周瑜  * @2018年5月7日 下午2:13:20  */ public final class JD ...

  4. 基于JDBC的数据库连接池高效管理策略

    2019独角兽企业重金招聘Python工程师标准>>> 介绍 在使用Java语言进行和数据库有关的的应用开发中,一般都使用JDBC来进行和数据库的交互,其中有一个关键的概念就是Con ...

  5. Java数据库连接池的实现(不用框架)

    前言:因为需要正式做项目,了解到了连接池这个东西.在网上找了很多资料,发现都是大同小异,各种转载,看上去搜出来了几十个答案,结果很可能是同一个.并且大多都是基于框架的数据库连接池.可是我只是想采用MV ...

  6. mysql连接池_基于Swoole的通用连接池 - 数据库连接池(life)

    open-smf/connection-pool 是一个基于Swoole的通用连接池,常被用作数据库连接池. 依赖 依赖版本PHP>=7.0.0Swoole>=4.2.9Recommend ...

  7. swoole mysql 连接池_基于Swoole的通用连接池 - 数据库连接池

    连接池 open-smf/connection-pool 是一个基于Swoole的通用连接池,常被用作数据库连接池. 依赖 依赖 版本 >=7.0.0 >=4.2.9 Recommend ...

  8. 基于MysqlConnector/C++的数据库连接池的实现

    From: http://blog.csdn.net/educast/article/details/14164097 1.连接池的介绍: 1.1应用背景: 一般的应用程序都会访问到数据库,在程序访问 ...

  9. c#打开数据库连接池的工作机制_数据库连接池-tomcat-jdbc使用笔记

    现在 主流的数据库连接池有:Proxool.C3P0.DBCP.tomcat-jdbc.Druid.其中tomcat-jdbc是tomcat服务器比较可靠的 数据库连接池. Tomcat 在 7.0 ...

最新文章

  1. Jquery源码中的Javascript基础知识(四)— jQuery.fn.init方法
  2. 【ios】NSMutableArray initWithContentOfFile 得到nil后无法进行addObject的问题
  3. Spark SQL JOIN操作代码示例
  4. MERGE语句——数据集横向合并
  5. python3解释器执行'ab2c3d'.lower().title()的结果是()_Python解释器执行'1234'.find('5')的结果是( )。...
  6. php版本越高越好么,php版本越高越好吗
  7. shop++源码反编译----随笔
  8. kubernetes视频教程笔记 (1)-什么是kubernetes和kubernetes的知识结构思维导图
  9. 总结之:CentOS 6.5 rsync+inotify实现数据实时同步备份
  10. vue json对象转数组_Vue优秀表单组件,用Vue构建表单的最简单方法——Vue Formulate
  11. Struts2通配符问题
  12. Go channel详解
  13. 计算机知识论,计算机科学与技术中的系统论与辩证法
  14. 宠物网页代码 html静态网页设计制作 dw静态网页成品模板素材网页 web前端网页设计与制作 div静态网页设计
  15. php执行who命令,Linux中的who命令实例介绍
  16. 语音端点检测c语言,语音端点检测的方法.ppt
  17. [ZT] 金融恐怖与国际安全系列之2009国际大动荡的起始点
  18. volatile,CAS,ABA三个关键字
  19. 2017年上海最新落户政策重磅出炉!你达标了吗?(明年就毕业了希望一切顺利)
  20. Pebble/RocksDB SST 文件详解

热门文章

  1. Excel 有哪些可能需要熟练掌握而很多人不会的技能?
  2. 边缘计算那些事儿--网络切片技术(2)
  3. 加里敦大学上学第二天总结
  4. 学习笔记:北极星指标OMTM
  5. LoRaWAN模块在车辆跟踪定位中的应用
  6. 【python】准点跑路人必备小程序~ 不信你用不到
  7. Vue实现仿淘宝商品详情属性选择的功能
  8. 网易云课堂 Machine Learning 编程作业 1:liner regression
  9. Spark基础知识梳理
  10. 第一章 容器核心知识概述