delphi uniDac

Delphi 2010安装及使用UniDAC 4.0

UniDAC是一个功能强大的非可视化跨数据库的数据访问组件,可用于Delphi,Delphi for .NET,C++Builder,and Lazarus (Free Pascal)。它提供了对流行数据库服务器的统一访问,像Oracle,Microsoft SQL Server,MySQL,InterBase,Firebird,PostgreSQL,SQLite,DB2,Microsoft Access,Sybase Advantage Database Server,Sybase Adaptive Server Enterprise,和其他数据库(使用ODBC驱动)。

从网上下载到UniDAC 4.0的源码版本,安装过程如下:
1.进入"...\unidac40src\Source\Delphi14"文件夹,找到"Make.bat"文件,打开并修改IDE目录路径,如下:
set IdeDir="%PROGRAMFILES%\Embarcadero\RAD Studio\7.0
call ..\Make.bat Delphi 14 WIN32
因为我这里Delphi 2010不是安装在默认位置,故修改如下:
set IdeDir="D:\Program Files\Embarcadero\RAD Studio\7.0
call ..\Make.bat Delphi 14 WIN32
2.执行"Make.bat"文件,自动执行一系列操作后,到"...\unidac40src\Bin\Delphi14"目录下,可发现库已经生成完毕;
3.运行Delphi 2010,菜单→"Tools"→"Options"→"Environment Options"→"Environment Variables",添加"...\unidac40src\Bin\Delphi14"完整路径到"PATH"环境变量;
4.菜单→"Component"->"Install Packages...","Add"添加"...\unidac40src\Bin\Delphi14"目录下的"dclunidac140.bpl";
5.菜单→"Tools"→"Options"→"Environment Options"→"Delphi Options"→"Library - Win32",在"Library Path"添加"...\unidac40src\Lib\Delphi14"路径;
6.此时,已可以使用UniDAC连接数据库了。若是需要链接查看源代码,将"...\unidac40src\Source"路径也添加到"Library Path"。

测试连接MySql数据库:
1.新建一个应用程序,在面板上拖动TUniConnection、TMySQLUniProvider、TUniQuery、TUniDataSource、TDBGrid到窗体上,如下图所示:

2.右键TUniConnection,选择"Connection Editor...",填入数据库连接参数,如下图所示:

3.因为MySql一般设置字符集为UTF-8,而Delphi 2010工程字符集是Unicode,在"Options"页面,设置"UseUnicode"为True,这可以通知服务器,客户端和服务器双方之间的所有数据都将通过UTF-8编码,设置这个选项会转换所有的TStringField字段类型到TWideStringField字段类型,使得几乎所有的语言符号都可以正确工作,但另一方面,也引起工作延迟。

4.关联其他项,在TUniQuery的SQL里面写"select * from city",设置Active为True,运行程序,可以看到符号都可以正常显示,如下图所示:

代码实现的方式如下:

procedure TForm1.FormCreate(Sender: TObject);
begin
UniQuery1.Connection := UniConnection1;
UniDataSource1.DataSet := UniQuery1;
DBGrid1.DataSource := UniDataSource1;
with UniConnection1 do
begin
ProviderName := 'MySQL';
Username := 'root';
Password := '123';
Server := '192.168.82.201';
Database := 'world';
Port := 3306;
SpecificOptions.Values['UseUnicode'] := 'True';
try
Connect;
UniQuery1.Close;
UniQuery1.SQL.Text := 'select * from city';
UniQuery1.Open;
except

end;
end;
end;
View Code

delphi2010下安装UniDac

1. 进入 Source/Delphi14,编辑Make.bat 批改IdeDir="D:/Program Files/Embarcadero/RAD Studio/7.0 为你的Delphi2010安装路径,重视:双引号只有前半项目组,没有后半项目组。
2. 履行Make.bat。完成后在当前目次生成一个Unidac的目次。
3. 批改文件夹属性,把所有文件夹的隐蔽属性去掉。(针对win7,winxp下可省略这一步)
4. 批改C:/Users/All Users/Documents/RAD Studio/7.0/Bpl的接见权限,只要能进入即可。办法如下:右键点击Documents,在属性的“安然”页中选“高等”。高等页面中有一项“拒绝”,选中它,点击”更改权限”,再点击”编辑”,选中”完全把握”,一路断定。(针对win7,winxp下可省略这一步)
5. .把Unidac目次下*.bpl复制到C:/Users/All Users/Documents/RAD Studio/7.0/Bpl下。(针对win7,winxp下可省略这一步)
6. 打开Delphi2010,从菜单Component->Install Packages 安装dclunidac140.bpl。
7. 把Source目次添加到delphi的library路径,操纵路径为:tools->options->environment options->Delphi options->library-win32,在library path中输入。(winxp下可省略这一步,因为第六步操纵完后已经在library path中参加路径)
8. 把Source/Delphi14/UniDAC/Lib目次添加到delphi的library路径。
9. OK,如今可以连接各类数据库啦

uniDAC用法总结

常言道,细微之处见体贴。UniDAC有一些过人的方法或属性。
比如,刷新单条记录(RefreshRecord)、多表更新的属性(UpdatingTable)、宏替换参数(Macros)、
集成删除/新增/修改/刷新/锁定SQL语句、FetchRows,更让人称道的是引入了UpdateSQL组件。
TUniTable、TUniQuery和TUniStoredProc是用来检索和编辑数据的UniDAC控件.
***************************TUniQuery******************************************************
UniDirectional属性
ADO没有单向数据集特性,所有的数据下载到本地,不停的开辟内存或释放大内存,对三层的内存是一个极大考验。TUniQuery有一个 UniDirectional属性,支持单向速度,这点和DBX的想法不谋而合。况且,单向数据集特性速度非常快,在三层中,配合 TDataSetProvider,中间件将其Data包发送到客户端,速度无可比拟。ADO也有流或XML格式包,但无论是XML或流格式,数据包远比 CDS的包大几倍。CDS封包技术很好!

FetchRows可以设定一次获取记录的行数
uniquery 和 unitable 的 SpecificOptions 属性,需要设置 FetchAll=False才能使 FetchRows 的设置生效,而默认情况下, oracle 是设置Oracle.FetchAll=False;
而对去 sql server 和 mysql 等,却是设置的 XXX.FetchAll=True
UniQuery.SpecificOptions.Values['FetchAll'] := BoolToStr(cbFetchAll.Checked, True);
UniDirectional属性
ADO没有单向数据集特性,所有的数据下载到本地,不停的开辟内存或释放大内存,对三层的内存是一个极大考验。TUniQuery有一个 UniDirectional属性,支持单向速度,这点和DBX的想法不谋而合。况且,单向数据集特性速度非常快,在三层中,配合 TDataSetProvider,中间件将其Data包发送到客户端,速度无可比拟。ADO也有流或XML格式包,但无论是XML或流格式,数据包远比 CDS的包大几倍。CDS封包技术很好!
UniDAC的单条记录刷新
1、设置 SQLRefresh.TEXT的刷新SQL,一般要具体到单条记录。比如:SQLRefresh.Text:='SELECT * FROM TName WHERE ID = :ID'(其中ID是表TName的主键,以确保返回只有一条记录)
2、设置 TRefreshOptions为 [roAfterInsert,roAfterUpdate],即为新增后刷新,修改后刷新。
3、调用 UniQuery1.RefreshRecord
如果不做以上设置,仅执行UniQuery1.RefreshRecord 是一点反映也没有的

UpdatingTable
属性 UpdatingTable 服务显示表将被更新(如果在查询中有许多表)。如果它的值为空,正在使用的表就会显示SQL操作的结果。 为了更好地对查询结果进行操作,建议应总是设置属性UpdatingTable

cachedupdates缓存更新
UniQuery默认状态为行提交,使用前根据需要设置readonly或cachedupdates属性

Filter过滤
UniQuery.Filter默认大小写区分,请注意设置FilterOptions属性([foCaseInsensitive]),TVirtualtable也存在相同情况

在UniQuery的SQL定义参数
我们经常会在UniQuery的SQL定义一些参数,在传参时,需要特别注意,例如:
QExec.Close;
QExec.SQL.Text:= ‘select * from YHB where sYHBH=_YHBH’;
在传参时有两种写法
1)最稳妥的写法
QExec. ParamByName(‘P_YHBH’).DataType:= ftString;
QExec. ParamByName(‘P_YHBH’).ParamType:= ptInPut;
QExec. ParamByName(‘P_YHBH’).AsString:= ‘张三’;(此处可将AsString换成Value)
2)下面这个写法我做了简单测试,也是可以的,但对复杂的SQL传参是否正确,未知
QExec. ParamByName(‘P_YHBH’).AsString:= ‘张三’;(在不对参数的数据类型和传入传出类型进行指定的情况下,绝对不能使用Value)

UniQuery.SetReadOnly属性
1) 我们经常会用到多表关联,且需要在前台修改数据。举个例子:a表和b表,在前台两个表字段都需要修改,则需要将SetReadOnly设置成false
2)特别注意:若将一个UniQuery.SetReadOnly设置成true,而这个表有一个自增长ID,那么你在提交数据时会出错,跟踪SQL会发现,ID被前台前行传了一个null值
UniQuery. RefreshRecord
可以刷新当前选择的数据
数据提交
数据提交的顺序,一定要注意:

with MyQuery do
begin
Session.StartTransaction;
try
... {Modify data}
ApplyUpdates; {try to write the updates to the database}
Session.Commit; {on success, commit the changes}
except
RestoreUpdates; {restore update result for applied records}
Session.Rollback; {on failure, undo the changes}
raise; {raise the exception to prevent a call to CommitUpdates!}
end;
CommitUpdates; {on success, clear the cache}
end;
View Code

对于单数据集的提交:
MyQuery. ApplyUpdates;
MyQuery. CommitUpdates;

Unidac:解决“trying to modify read-only Field”问题!
后台使用SQL语句中,经常会关联自定义函数或视图,而CDS(TClientDataSet)对字段校验比较严格,涉及到的自定义函数或视图输出的字段,都会强制改为ReadOnly为True属性。
当后台使用UniDAC+CDS,关联视图或自定义函数,为了数据一致性,有可能需要在前台界面上修改CDS相关的自定义函数输出的字段,即便是将 该字段设置为 readonly为false,

或将其字段的 FieldDefs属性的attributes的faReadOnly去掉,系统也会抛出一个异常:trying to modify read-only Field。
解决问题很简单,将TUniQuery.Options.SetFieldsReadOnly为false即可。
查一下TUniQuery.Options.SetFieldsReadOnly的帮助,这样写道:
If True, dataset sets the ReadOnly property to True for all fields that do not belong to UpdatingTable or can not be updated. Set this option for datasets
that use automatic generation of the update SQL statements only.
Macros属性
要注意:如果要替换的值是一个字符串,那么记得在字符串两边加''号,因为Macro只是一个简单的替换功能,他不会去判断条件的类型。
其他
UniQuery默认情况下,有些varchar类型的字段有自动加了一个空格,请注意设置Options.TrimVarChar=true
UniQuery在进行Insert时,若字段不能为null且前台操作未填写时,可能会报错,请设置RequiredFields=true
UniQuery在修改数据集时,默认的方式是按关键字生成SQL语句进行数据提交。还有另外两种方式:一是设置updateSQL,一是设置KeyFields(具体请sql跟踪查看)
UniQuery的数据排序属性是IndexFieldNames

********************存储过程*************************************************************************
TUniConnection, TUniSQL, TUniQuery, TUniStoredProc均可以执行存储过程。
TUniConnection:
是一种最简单的执行存储过程的控件,但他有很多限制。TUniConnection不能具有SQL、存储过程名和参数,不支持输出参数也不支持存储执行的预准备。当然,如果只是运行一个既没有返回也没有输出参数设置,那TUniConnection是一个不错的选择。
TUniSQL:TUniSQL是一个被分离出的小控件,执行SQL语句但不返回结果集。它没有数据存储,但要消耗一些内存,但比TUniQuery和TUniStoredProc的执行速度快。
UniSQL.SQL.Text :=str
UniSQL.Execute;
s := 'Rows affected: ' + IntToStr(UniSQL.RowsAffected);

TUniQuery:TUniQuery除具有TUniSQL的执行功能外,还能返回结果集。
TUniStoredProc:TUniStoredProc是专门用来执行存储过程的一个控件,可以返回结果集、输出参数、执行准备以及通过CreateProcCall方法初始化等。
1、 参数类型
UniDAC支持四种参数类型:input, output, input/output, result
***********************************************TUniConnection************************************************************
事务
TUniConnection通过StartTransaction, Commit, Rollback等方法来控制本地事务,判断一个事务是否开启用InTransaction。
1、TUniConnection
建立和控件数据连接的控件,能访问的数据库包括:Oracle, SQL Server, MySQL, InterBase, Firebird, 和PostgreSQL.
虽然UniDAC对不同的数据库提供了统一的访问接口,但是对个别数据库还是要进行一些特殊的设置,这些设置是一个字符串列表,你可以按以下代码进行设置:
UniConnection.SpecificOptions.Values['CharLength'] := '1';
1、 Oracle

 

unidac 怎么对数据的插入和更新

1、使用unidac 如何得到有返回是否插入数据成功

2、使用unidac 如何得到有返回是否更新数据成功

下面是我关于数据查询和无返回结果的插入数据,求教如何处理上面的两个问题
--数据的查询

with form1.UniQuery1 do
form1.UniQuery1.Close;
form1.UniQuery1.SQL.Clear;
form1.UniQuery1.SQL.Add('select TareNo 皮重号,TareWeigth*0.001 重量 from umttare_tmp');
form1.UniQuery1.Open;
View Code

--无返回值的插入数据

form1.UniQuery4.Close;
form1.UniQuery4.SQL.Clear;
form1.UniQuery4.SQL.Text:='insert UMTTare_Tmp (ScaleID,TareNo,TareWeigth,dummy) values(0,:a,:b,null)';
form1.UniQuery4.ParamByName('a').Value := form1.Edit1.Text;
form1.UniQuery4.ParamByName('b').Value := form1.Edit2.Text;
form1.UniQuery4.ExecSQL;


UniTable2.Open;
UniTable2.Append;
TBlobField(UniTable2.FieldByName('CONTENT')).LoadFromFile('d:\PackageRequires.exe');
UniTable2.Post;

UniQuery1.Close;
UniQuery1.SQL.Text := 'SELECT * FROM TESTBLOB WHERE ID=9';
UniQuery1.ExecSQL;
TBlobField(UniQuery1.FieldByName('CONTENT')).SaveToFile('d:\system\desktop\d.exe');
View Code

unidac直连oracle导入图片到表中

var
  UniConnection: TUniConnection;
  UniQuery: TUniQuery;
begin
  UniConnection:= TUniConnection.Create(nil);
  UniQuery:= TUniQuery.Create(nil);
  UniConnection.ProviderName := 'ORACLE';   
  UniConnection.SpecificOptions.Clear;
  UniConnection.SpecificOptions.Values['Direct'] := 'True';
  UniConnection.Server := 'host:port:database';  
  UniConnection.Username := 'username';
  UniConnection.Password := 'passwd';
  UniConnection.Connect;
  UniQuery.Connection := UniConnection;
  UniQuery.SQL.Text := 'insert into temp3 (a4,a6) values (:a4,:a6)';
  UniQuery.ParamByName('a4').AsString := 'pic1';
  UniQuery.ParamByName('a6').ParamType := ptInput; //这句是必要的
  UniQuery.ParamByName('a6').LoadFromFile('1.jpg',ftOraBlob);  
  UniQuery.Execute;
end;
 
 
如果加这句UniConnection.SpecificOptions.Values['UseUnicode'] := 'True';就执行报错
 
View Code

基于UniDac的数据库连接池

上篇提到了在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.
View Code

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
View Code

实现代码:

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.
View Code

 

Delphi中SQLite如何读写二进制字段(Blob类型)

在Delphi中,有大量的组件可以操作SQLite数据库,如UniDAC就是其中一个比较优秀的,当然还有ASQLite3Components,也有SQLite3版的ODBC驱动,可直接用ADO操作。本文简要说明SynopseSQLite3读写二进制字段,先说下SynopseSQLite3的优点,静态编译集成SQLite3引擎,不需要额外的DLL支持,支持SQLite3加密,支持JSON表,支持网络版的SQLite3.支持线程安全保护.

首先建一个表,字段类型为BLOBSL.Add('CREATE TABLE IF NOT EXISTS TEST (');

SL.Add('FileName TEXT PRIMARY KEY,');

SL.Add('Data BLOB);');

SL.Add('');
复制代码写入二进制数据:var

  FFileStream:TFileStream;

  FData:array of Char;

  FSQLR:TSQLRequest;

  ASQL:AnsiString;

begin

  FFileStream:=TFileStream.Create('test.xml',fmOpenReadWrite);

  ASQL:='INSERT INTO TEST(FileName,Data) VALUES('+QuotedStr('test.xml')+',?)';

  try

    SQLite数据库对象.Execute('DELETE FROM TEST');

    //

    SetLength(FData,FFileStream.Size);

    FFileStream.Position:=0;

    FFileStream.Read(PChar(FData)^,Length(FData));

    FSQLR.Prepare(SQLite数据库对象.DB,ASQL);

    FSQLR.Bind(1,PChar(FData),Length(FData));

    FSQLR.Execute;

  finally

    FreeAndNil(FFileStream);

  end;
复制代码其中问号是参数,SQLite数据库对象类型为TSQLDataBase

读取数据并写入到文件:var

  FBlobField:TSQLBlobStream;

  FFileStream:TMemoryStream;

  FData:array of Char;

begin

  try

    FFileStream:=TMemoryStream.Create;

    FBlobField:=SQLite数据库对象.Blob('','TEST','Data',1,True);

    try

      FBlobField.Position:=0;

      SetLength(FData,FBlobField.Size);

      FBlobField.ReadBuffer(PChar(FData)^,FBlobField.Size);

      FFileStream.Write(PChar(FData)^,FBlobField.Size);

      FFileStream.SaveToFile("test.xml");

    finally

      FreeAndNil(FBlobField);

      FreeAndNil(FFileStream);

    end;

  except

    Result:='';

  end;
View Code

UniDAC使用日记

UniDAC使用日记
1. UniQuery默认状态为行提交,使用前根据需要设置readonly或cachedupdates属性
2. UniQuery.Filter默认大小写区分,请注意设置FilterOptions属性([foCaseInsensitive]),TVirtualtable也存在相同情况
3. UniQuery默认情况下,有些varchar类型的字段有自动加了一个空格,请注意设置Options.TrimVarChar=true
4. UniQuery在进行Insert时,若字段不能为null且前台操作未填写时,可能会报错,请设置RequiredFields=true
5. UniQuery在修改数据集时,默认的方式是按关键字生成SQL语句进行数据提交。还有另外两种方式:一是设置updateSQL,一是设置KeyFields(具体请sql跟踪查看)
6. UniQuery的数据排序属性是IndexFieldNames
7. 数据提交的顺序,一定要注意:

with MyQuery do
begin
Session.StartTransaction;
try
... {Modify data}
ApplyUpdates; {try to write the updates to the database}
Session.Commit; {on success, commit the changes}
except
RestoreUpdates; {restore update result for applied records}
Session.Rollback; {on failure, undo the changes}
raise; {raise the exception to prevent a call to CommitUpdates!}
end;
CommitUpdates; {on success, clear the cache}
end;
View Code

对于单数据集的提交:
MyQuery. ApplyUpdates;
MyQuery. CommitUpdates;
8. DataM中提供了一个功能Clone(Source, Dest):一、Dest是TVirtualTable,则完整的将源数据集复制;二、Dest是UniQuery,则是将Source的SQL复制到Dest,并Open
9. UniQuery.SetReadOnly属性说明
1) 我们经常会用到多表关联,且需要在前台修改数据。举个例子:a表和b表,在前台两个表字段都需要修改,则需要将SetReadOnly设置成false
2) 特别注意:若将一个UniQuery.SetReadOnly设置成true,而这个表有一个自增长ID,那么你在提交数据时会出错,跟踪SQL会发现,ID被前台前行传了一个null值
10.UniQuery. RefreshRecord,可以刷新当前选择的数据
11.我们经常会在UniQuery的SQL定义一些参数,在传参时,需要特别注意,例如:
QExec.Close;
QExec.SQL.Text:= ‘select * from YHB where sYHBH=:P_YHBH’;
在传参时有两种写法
1)最稳妥的写法
QExec. ParamByName(‘P_YHBH’).DataType:= ftString;
QExec. ParamByName(‘P_YHBH’).ParamType:= ptInPut;
QExec. ParamByName(‘P_YHBH’).AsString:= ‘张三’;(此处可将AsString换成Value)
2)下面这个写法我做了简单测试,也是可以的,但对复杂的SQL传参是否正确,未知
QExec. ParamByName(‘P_YHBH’).AsString:= ‘张三’;(在不对参数的数据类型和传入传出类型进行指定的情况下,绝对不能使用Value)
UniDAC的过程中一定要注意,在DataM中提供了一个函数PrepareParam,请注意看一下。à在ADO
12.Sybase的一个特性
update a set a.fSJ=b.fSJ
from table1 a join table2 b on a.sYPBSM=b.sYPBSM
上述SQL语句在Sybase下执行不能通过,请改写成如下SQL(在MS和ASE中都能执行通过)
update table1 set fSJ=b.fSJ
from table1 a join table2 b on a.sYPBSM=b.sYPBSM


UniDAC官方网址:http://www.devart.com/
一、连接数据库Connecting to Database
通用连接属性


Provider
第一个就应该设置的属性,指定要连接的数据库类型,根据指定的数据库连接类型不同其它的设置也会发生相应的变化;
Username and Password
登录数据的有效用户名和密码;
Server
通常将此设成要连接数据库所在的计算机名或IP地址,如果将此属性设置为空,对于MySQL, InterBase连接数据库方式, UniDAC 将试图连接本地(Localhost)。

Oracle:
客户端模式下,指定的Server名称一定要出现在tnsnames.ora中,且有效,设置效果如下:

with UniConnection1 do
begin
ProviderName := 'Oracle';
Server := 'ORCL';
Username := 'username';
Password := 'password';
end;
View Code

直连模式(Direct mode)下:在此模式下,运行软件的计算机可以不安装Oracle客户端而连接Oracle数据库,但要做以下设置:
设置直连模式



设置Server
指定Server格式:Host:Port:SID.
注:客户端模式和直连模式的设置不能混淆,否则无法执行。两种格式严格区分。
效果如下:

with UniConnection1 do
begin
ProviderName := 'Oracle';
Server := '192.168.1.113:1521:ORCL';
Username := ' Username ';
Password := ' Password 3';
end;
View Code

SQL Server:
指定要连接的数据库所在的网络IP,且有效,如果采用的不是默认端口(1433),Server应该这样设置:HostName,PortNumber.
Database
这个属性只对SQL Server, MySQL, PostgreSQL, InterBase, and SQLite 连接方式有效,
Port
指定TCP/IP协议访问的有效端口
MySQL – 默认端口 3306
PostgreSQL -默认端口5432
二、UniDAC(版本:2.7)连接数据库必要文件或局限需求
1、Oracle连接
Oracle在采用直连模式将受到以下限制:
特别要注意的是(包括:应用TUniLoader):
1)、应用直连模式,本地执行程序的客户机必须要安装TCP/IP协议
2)、注意防火墙
3)、数据库的triggers、check constraints、clustered tables、loading of remote objects、user-defined types将得不到支持
4)、available, like OBJECT, ARRAY, REF, XML, BINARY_DOUBLE, BINARY_FLOAT这些类型将变为不可用
5)、TUniLoader的应用版本应高于Oracle client 8.17
以下是英文原文:

triggers are not supported
check constraints are not supported
referential integrity constraints are not supported
clustered tables are not supported
loading of remote objects is not supported
user-defined types are not supported
LOBs must be specified after all scalar columns
LONGs must be specified last
You cannot use TUniLoader in a threaded OCI environment with Oracle client 8.17 or lower.
Connect using the TCP/IP network protocol only.
Some types are not available, like OBJECT, ARRAY, REF, XML, BINARY_DOUBLE, BINARY_FLOAT.
Certain problems may occur when using firewalls.
NLS conversion on the client side is not supported.
Transparent Application Failover is not supported.
Statement caching is not available.
OS authentication and changing expired passwords features are not available.
The DES authentication is used.
Oracle Advanced Security is not supported.
We do not guarantee stability of multithreaded applications. It is highly recommended to use the separate TUniConnection component for each thread when using UniDAC from different threads.
View Code

 

SQL Server连接
提供连接支持
服务端: SQL Server 2005 (including Compact and Express editions), SQL Server 2000, SQL Server 7, and MSDE.
客户端: SQL OLE DB and SQL Native Client.
环境需求
MDAC(Microsoft Data Access Components)版本不低于2.5
My SQL连接
My SQL提供的连接也包括直连和客户端连接方式,默认采用的是直连方式,如果要更改连接方式,在应用程序分发时要带上libmysqld.dll
二、Delphi与各数据库数据类型比较
Delphi数据类型与各数据库数据类型对比如下表,如有具体说明见表中脚注:

Delphi Type Oracle Types SQL Server Types MySQL Types [1] InterBase Types PostgreSQL Types SQLite Types
ftSmallint NUMBER(p, 0)[2] (p < 5) SMALLINT TINYINT(M) (M > 1)SMALLINT SMALLINT SMALLINT TINYINTSMALLINT
ftWord - TINYINT TINYINT(M) UNSIGNED (M > 1)SMALLINT UNSIGNEDYEAR - - -
ftInteger NUMBER(p, 0)[2](4 < p < 10) INT MEDIUMINTMEDIUMINT UNSIGNEDINT INTEGER INTEGER INTEGERINT
ftLargeint NUMBER(p, 0)[2] (9 < p < 19) BIGINT BIT INT UNSIGNEDBIGINTBIGINT UNSIGNED BIGINT BIGINT BIGINT
ftFloat NUMBER(p, s)[2] BINARY FLOAT(FLOAT)BINARY DOUBLE DECIMAL(p, s)[3] FLOATREAL DECIMAL(p, s)[3] FLOATDOUBLE NUMBER(p, s)[3] FLOATDOUBLE PRECISION DECIMAL[3] REALDOUBLE PRECISION DECIMAL(p, s)[3]FLOATDOUBLE PRECISION
ftBCD NUMBER(p, s)[2] (p < 15) and (s < 5) DECIMAL(p, s)[3] (p < 15) and (s < 5) DECIMAL(p, s)[3] (p < 15) and (s < 5) DECIMAL(p, s)[3] (p < 15) and (s < 5) DECIMAL[3] DECIMAL[3]
ftFMTBcd NUMBER(p, s)[2] (14 < p < 39) and> (4 < s < 39) DECIMAL(p, s) (14 < p < 39) and (4 < s < 39) DECIMAL(p, s)[3] (14 < p < 39) and(4 < s < 39) DECIMAL(p, s)[3] (14 < p < 19) and(4 < s < 19) DECIMAL[3] DECIMAL[3]
ftCurrency - MONEYSMALLMONEY - - MONEY MONEY

ftBoolean - BIT TINYINT[4] BOOL[4] BOOLEAN[4] BOOLEAN BOOLEAN BOOLEAN

ftString VARCHAR2NVARCHAR2VARCHARCHARNCHARRAW[5] INTERVAL DAY TO SECONDINTERVAL DAY TO MONTHROWIDUROWID CHARVARCHAR CHARVARCHARENUMSETBINARY[6] VARBINARY[6] CHARVARCHAR CHARVARCHAR CHARVARCHAR
ftWideString See note [7] NCHARNVARCHAR See note [7] See note [7] See note [7] See note [7]
ftMemo LONGAlso see note [8] TEXTNTEXT[9] TINYTEXTTEXTMEDIUMTEXTLONGTEXT BLOB TEXT TEXT TEXTCLOB
ftWideMemo See note[10] NTEXT[11] See note[10] See note[10] See note[10] See note[10]
ftOraClob CLOBNCLOB - - - - -

ftBlob LONG RAW IMAGE TINYBLOBBLOBMEDIUMBLOBLONGBLOBSpatial Data Types BLOB BINARY BYTEA BLOB
ftOraBlob BLOB - - - LARGE OBJECT -
ftBytes - BINARYTIMESTAMP BINARY - - -
ftVarBytes RAW VARBINARY VARBINARY CHARVARCHAR(CHARSET = OCTETS) - BINARYVARBINARY

ftDate - - DATE DATE DATE DATE
ftDateTime DATE DATE DATETIME TIMESTAMP TIMESTAMP TIMESTAMPDATETIME
ftTime - - TIME TIME TIME TIME
ftTimeStamp TIMESTAMPTIMESTAMP WITH TIMEZONE - - - - -

ftCursor REF CURSOR - - - REFCURSOR -
ftGuid - UNIQUEIDENTIFIER - - - -
ftVariant - SQL_VARIANT - - - -

NOT SUPPORTED BFILEOBJECTXML CURSORXMLTABLE - - - -
[1] – 如果FieldsAsString 选项被设置 True,则除BLOB和TEXT数据类型外,全部做为ftString来处理
[2] – Oracle NUMBER数据类型与Delphi数据类型对应方式:
if scale equals zero, provider checks values of the specific options to choose the correct Delphi type in the following order:
1.1 field precision is less or equal Precision Smallint (default is 4) - uses ftSmallint;
1.2 field precision is less or equal Precision Integer (default is 9) - uses ftInteger;
1.3 field precision is less or equal Precision LargeInt (default is 18) - uses ftLargeint;
if scale is greater than zero, the appropriate Delphi type is chosen using the following sequence of rules:.
2.1 field precision is less or equal PrecisionFloat (default is 0) - uses ftFloat;
2.2 EnableBCD is True and field precision, scale is less or equal PrecisionBCD (default is 14,4) - uses ftBCD;
2.3 EnableFMTBCD is True and field precision, scale is less or equal PrecisionFMTBCD (default is 38,38) - uses ftFMTBCD;
2.4 uses ftFloat.
[3] - The appropriate Delphi type is chosen using the following sequence of rules:
EnableBCD is True and field precision, scale is less or equal 14,4 - uses ftBCD;
EnableFMTBCD is True - uses ftFMTBCD;
uses ftFloat.
[4] - If the EnableBoolean option is True
[5] - If the RawAsString option is True
[6] - If the BinaryAsString is True
[7] - If the UseUnicode option is True, all server types mapped to ftString will be mapped to ftWideString.
[8] - If the LongStrings option is False, and the field length is greater than 255, all server types mapped to ftString will be mapped to ftMemo.
[9] - For all Delphi versions prior to BDS 2006.
[10] - If the UseUnicode option is True, in BDS 2006 and later versions all server types mapped to ftMemo will be mapped to ftWideMemo.
[11] - For BDS 2006 and higher IDE versions.
View Code

三、UniDAC更新数据
1、数据自动更新
TUniTable、TUniQuery和TUniStoredProc是用来检索和编辑数据的UniDAC控件,
四、主从关系表
五、存储过程
1、TUniConnection, TUniSQL, TUniQuery, TUniStoredProc均可以执行存储过程。
TUniConnection:
是一种最简单的执行存储过程的控件,但他有很多限制。TUniConnection不能具有SQL、存储过程名和参数,不支持输出参数也不支持存储执行的预准备。当然,如果只是运行一个既没有返回也没有输出参数设置,那TUniConnection是一个不错的选择。
TUniSQL:
TUniSQL是一个被分离出的小控件,执行SQL语句但不返回结果集。它没有数据存储,但要消耗一些内存,但比TUniQuery和TUniStoredProc的执行速度快。
TUniQuery:
TUniQuery除具有TUniSQL的执行功能外,还能返回结果集。
TUniStoredProc:
TUniStoredProc是专门用来执行存储过程的一个控件,可以返回结果集、输出参数、执行准备以及通过CreateProcCall方法初始化等。
参数类型
UniDAC支持四种参数类型:input, output, input/output, result
六、事务处理
TUniConnection通过StartTransaction, Commit, Rollback等方法来控制本地事务,判断一个事务是否开启用InTransaction。
七、控件详解
1、TUniConnection
建立和控件数据连接的控件,能访问的数据库包括:Oracle, SQL Server, MySQL, InterBase, Firebird, 和PostgreSQL.
虽然UniDAC对不同的数据库提供了统一的访问接口,但是对个别数据库还是要进行一些特殊的设置,这些设置是一个字符串列表,你可以按以下代码进行设置:
UniConnection.SpecificOptions.Values['CharLength'] := '1';
Oracle

 


多线程下使用使用 UniDAC+MSSQL
ADO线程不安全,UniDAC 在使用MSSQL也是如此。其实这是微软COM问题,不怪Devart公司。
一般解决方法是在线程开始启用 CoInitialize(nil),线程结束调用 CoUninitialize 。如果你使用多种数据库连接,比如三层中经常切换到MSSQL和Oracle,我们只需在判断 TUniConnection 的连接前事件 OnBeforeConnect 写下如下代码:

procedure TServDBFunc.ServConnBeforeConnect(Sender: TObject);
begin
if (ServConn <> nil) and SameText(ServConn.ProviderName,'SQL Server');then
CoInitialize(nil);
end;
在TUniConnection 的关闭连接后事件 OnAfterDisconnect 写下如下代码:
procedure TServDBFunc.ServConnAfterDisconnect(Sender: TObject);
begin
if (ServConn <> nil) and SameText(ServConn.ProviderName,'SQL Server');then
CoUninitialize;
end;
View Code

需要注意的是,必须先判断连接控件 (ServConn <> nil) 是否为空,否则,你会陷入指针释放的问题。

unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBAccess, Uni, MemDS, UniProvider, InterBaseUniProvider;
type 
TForm1 = class(TForm)
UniConnTest: TUniConnection;//用于数据库的连接
InterBaseUniProTest: TInterBaseUniProvider;//ib/FB的数据提供
UniQryTest: TUniQuery; 
UniDataSrTest: TUniDataSource; 
DBGrid1: TDBGrid; 
Button1: TButton; 
Button2: TButton; 
procedure Button1Click(Sender: TObject); 
procedure Button2Click(Sender: TObject); 
private
{ Private declarations } 
public 
{ Public declarations } 
end; 
var 
Form1: TForm1; 
implementation 
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin 
with UniConnTest do
begin 
ProviderName := 'interbase';//这里确定为interbase 但是可以支持firebird 
Password := 'masterkey';//数据库密码 
Username := 'sysdba';//数据库密码 
Server := ''; 
Database := 'TD_HOUSE.FDB';//数据库文件的位置,这里在当前目录 SpecificOptions.Clear;
SpecificOptions.Add('InterBase.ClientLClientLClientLClientLibraryibraryibraryibrary=fbembed.dll');
//设置设置设置设置embeddll驱动位置 
SpecificOptions.Add('InterBase.CharLength=0')
;//设置设置设置设置为0让,
unidacunidacunidacunidac自动读取fb设置设置设置设置 SpecificOptions.Add('SQLDialet=3');
//设置设置设置设置为3 
SpecificOptions.Add('USEUnicode=true');//迟滞unicode 有人说有问题 我没有发现 
try 
Connect; 
ShowMessage('OK'); 
except 
ShowMessage('eer'); 
end; 
end;
end; 
procedure TForm1.Button2Click(Sender: TObject);
begin 
UniQryTest.Close; 
UniQryTest.SQL.Text := 'select * from TB_SYS_LOG'; //
UniQryTest.FetchingAll; 
UniQryTest.DisableControls; 
UniQryTest.Open; 
UniQryTest.EnableControls;
end; 
end.
View Code

有网友问我常用的控件及功能。我先大概整理一下,以后会在文章里面碰到时再仔细介绍。
Devexpress VCL 这个基本上覆盖了系统界面及数据库展示的方方面面,是做桌面系统必备的一套控件,目前的版本是2011.2.3, 支持win32 及win64。
AutoUpgrader 这个是自动更新的一个小控件,适合桌面程序自动更新,但是自从2007 年后,就没有更新了,我对其进行了修改,使其可以安装在delphi XE2 上,
同时支持win64。
Devart 公司出品的UniDAC,ODAC,SDAC,IBDAC, 这几个是目前delphi 数据库存取最好的控件,UniDAC 几乎支持所有的数据库存取,而后面几个则是针对每种
提供专用的访问和控制功能,尤其是ODAC ,可以直接使用TCP/IP 连接oracle,免去安装Oracle客户端的麻烦,非常适合各种场合应用(在xe2 里面,已经可以直接支持用
Iphone 访问oracle了)。
Advanced Data Export 和 Advanced Data Import 这是EMS 公司出品的数据导入、导出控件,几乎可以导入、导出常用的各种数据格式,是数据库转换和备份的必备控件。
NativeXml 是生成和解析XML 文件及格式一个非可视控件(使用时,直接引用单元),是一个轻量级的xml 解析器,支持windows 和linux, 以前是收费的,
现在是开源了(唉,可惜我当时的银子了)。
Paxcompiler 是目前最快的,最稳定的Pascal 脚本解析器,我前面介绍的delphi web 脚本就是使用它做解释器的,目前还不支持win64,据作者说今年元月底就会支持win64.
kbmMW 是目前唯一与Remobjects 并驾齐驱的delphi多层解决方案, 比Remobjects 紧凑、便宜,但是功能绝不输给Remobjects。我后面后介绍使用kbmMW 实现各种多层应用。
TeeChart Pro 在delphi 的图形显示方面目前唯一的选择,虽然从delphi 3 就随delphi 捆绑发布,但是捆绑的是标准版,很多功能都不全,要展示各种绚丽的统计、分析功能,
还是要用专业版(呵呵,领导们都是喜欢这个的)。最新的TeeChart Pro 已经支持firemonkey了(兼容性还有点问题),你可以把pc 上的绚丽图形放到Iphone 上了。
从 Delphi 1 开始,delphi 每个版本都会有报表工具,但是每次自带的报表工具都相当不给力,几乎没有人使用,因此出现了几个非常不错的第三方报表工具,例如Reportbuilder,
Fastreport 等,由于今年出的delphi xe2 开始捆绑Fastreport 标准版,同时Reportbuilder 的价格高的离谱,而且fastreport 确实非常不错,建议报表工具还是用Fastreport.
Delphi 目前应该还是windows 平台非常强的开发工具,因此经常会用来写windows 服务,虽然Delphi 本身支持winservice 的开发,但是功能也就是能开发而已,要开发专业的
winservice, 还是要用Svcom , 这个可以像普通程序一样调试服务程序,同时支持 界面和服务在同一个程序里面,调试、配置、安装都非常方便。
这几年随着web 技术的发展,各种JS 框架越来越多,与delphi 后台结合,基本上都需要使用JSON 格式,delphi XE 开始支持JSON ,但是个人认为语法复杂,理解困难,
幸好有开源的Super Object Toolkit,非常直观和简单,在服务器端运行也非常稳定,我上面的文章也有介绍。
以上就基本上是我常用的控件,当然还有些控件由于比较偏,就不再做介绍了。我个人的原则是只要有现成的控件,我不会再去做类似的,毕竟个人精力和能力有限,而人家专业做控件的,
无论从质量和功能上都有保证,同时只要是正版用户,作者都会很及时的修改bug 的,自己把精力放到实现第三方无法实现的地方就可以了

UniDAC使用SQLite数据库可能碰到的问题

如果说要使用第三方控件来链接操作数据库,我想UniDAC绝对是个很好的选择。对于SQLite来说,像这样能较好支持中文的第三方控件更是少有了。不过使用UniDAC来说可能会碰到一些有趣的问题,特别是对于新手来说。现在说说我安装控件后使用SQLite碰到的问题:

一、SQLite3.dll 不能被加载的问题
开始以为只要在程序主目录下就可以直接在链接时被调用,但其实不是这样的,默认情况下一般是从C:/windows/system32目录下调用的,至少对于编程工具来说这是默认的调用路径,所以将dll拷到目录下即可解决,这个其实也不算是什么问题。

二、UniDAC这个控件很有意思,在链接数据库时是不提供打开文件对话框的,如果你直接填写数据库名的话,这个数据库应该会是直接被建立在桌面上了。

三、数据库不能被打开,Database could not be Opened!
据说从3.5版本以后,SQLite是不支持中文路径的,不过我用3.5以上版本也能链接成功,当然,这是在解决问题之后,解决之前的问题是如果把路径设置为英语,那绝对是链接没问题,换中文的话打死也链接不上,所以我想这方面多少应该是有些关系的。

四、SQLite function is not linked!
SQLite的函数未链接,说白了就是函数调用失败,我不知道其它人碰到会是什么样的原因,但我这里碰到的应该是sqlite3.dll损坏造成的函数调用失败,不能说是肯定,但在替换过两次后,链接恢复正常。想了想,用sqlite3.dll的肯定不只一种软件,如果你安装这类软件时,软件没有检查sqlite3.dll的存在和版本,并且直接替换掉了你原来使用的dll,那就很有可能造成版本不兼容问题,从而导致数据库链接失败,至少我看到了,当我恢复我原来的dll时,另一个原来使用正常的软件挂掉了……

五、不是问题的问题,用UniDAC设置链接时我经常会看到unsuppored metadata kind ,看上去不是什么特别的异常,应该只是设置链接的步骤不对,先把conneted设置为False再操作就OK了。

让unidac支持加密的sqlite

让unidac支持加密的sqlite
sqlite是一款优秀的单文件数据库软件,只需一个dll就就完全包含数据库引擎的功能,而且可以嵌入至其它程序中,完全不用额外的设定。其特性如下:
* 支持ACID (Atomic, Consistent, Isolated, Durable) 交易。
* 零组态设定(Zero-configuration),无须管理者的设定及管理。
* 支持大部分SQL92的语法。
* 数据库存在于一个单一的文件中。
* 数据库系统所在机器的字节顺序(Byte order)无关。
* 支援大小至2 TB (2^41 bytes)。
* 极小的内存需求:小于3万行的C语言程序代码。小于250KB的程序空间。
* 大部分的数据库操作皆快于一般流行的数据库系统。
* 简单易用的API。
* 支援TCL。也有其它语言的支持可用。
* 注释详细的程序代码,以及超过90%的测试。
* 链接库自己包含完整的功能,无须其它额外的程序或链接库。
* 程序代码版权为public domain。任何用途皆可免费使用。
不过sqlite开源版是不支持加密的,只留有接口,没有进行实现,不过开源界从来不缺乏雷锋,于是有了wxsqlite工程,wxsqlite是 sqlite的wxWidgets c++实现接口,顺便用aes加密算法实现了sqlite的加密功能,与sqlite 100%兼容,实在是赞!
我常用的数据库连接组件是unidac,最新的3.x版本已经支持sqlite,不过也没有实现加密函数定义,于是自已动手帮它加上,我用的版本是3.0.0.6。
\UniProviders\SQLite\LiteCallUni.pas是调用接口定义
// functions下面加上以下代码:
_sqlite3_key = function(
pDb: Tsqlite3; // Database handle
pKey: PAnsiChar; // Database PassWord (UTF-8)
nKey: Integer // Database sizeofpassword
): integer; {$IFNDEF CLR}cdecl;{$ENDIF}

_sqlite3_rekey = function(//If the current database is not encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the database is decrypted.
pDb: Tsqlite3; // Database handle
pNew: PAnsiChar; // Database New PassWord (UTF-8)
nNew: Integer // Database sizeofnewpassword
): integer; {$IFNDEF CLR}cdecl;{$ENDIF}
...........................

var 下面加

sqlite3_key: _sqlite3_key;
sqlite3_rekey: _sqlite3_rekey;
........................................

procedure InitFunctions;
begin
{$IFDEF CLR}下面加
sqlite3_key := {$IFNDEF UNIDACPRO}LiteCallCLR{$ELSE}LiteCallCLRUni{$ENDIF}.sqlite3_key;
sqlite3_rekey := {$IFNDEF UNIDACPRO}LiteCallCLR{$ELSE}LiteCallCLRUni{$ENDIF}.sqlite3_rekey;
...................

{$ELSE}下面加
sqlite3_key := GetProc('sqlite3_key');
sqlite3_rekey := GetProc('sqlite3_rekey');

..................................
initialization
LockInit := TCriticalSection.Create;
{$IFNDEF CLR}下面加
sqlite3_key := @NotLink;
sqlite3_rekey := @NotLink;
..................................
\UniProviders\SQLite\LiteClassesUni.pas是引擎调用接口实现
Check(sqlite3_open(PAnsiChar(UTF8Encode(FDatabase)), FSQLite));
下面加:

if Pos('ChangePassword',FUsername)=1 then
begin
if Copy(FUsername,16,Length(FUsername))<>'' then
SQLite3_key(FSQLite,PAnsiChar(Copy(FUsername,16,Length(FUsername))),SizeOf(FUsername));
if FPassword<>'' then
SQLite3_rekey(FSQLite,PAnsiChar(FPassword),SizeOf(FPassword));
end
else if FUsername='ClearPassword' then
begin
if FPassword<>'' then
SQLite3_key(FSQLite,PAnsiChar(FPassword),SizeOf(FPassword));
SQLite3_rekey(FSQLite,0,0);
end
else
if FPassword<>'' then
Check(SQLite3_key(FSQLite,PAnsiChar(FPassword),SizeOf(FPassword)));
View Code

测试加密,解密正常。

用UniQuery添加数据

可以直接添加blob sub_type 1类型的字符串数据,如下面的text字段为blob sub_type 1.虽然UniTable也可以实现同样的功能,但由于其要载入所有记录内存,所以效率不是太好.而UniQuery.Params.ParamByName方法则不能直接添加blob sub_type 1类型的数据,也不能用loadfromstream(ms)进行加载数据,所以不行.
procedure TForm1.FormCreate(Sender: TObject);
begin
self.UniConnection1.Connect;
UniQuery1.Close;
UniQuery1.SQL.Clear;
UniQuery1.SQL.Text:='select * from tableblob where 0=1'; {返回空数据集,避免加载所有记录到内存,提高效率}
UniQuery1.Open;
UniQuery1.Prepared:=true;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
istart:=GetTickcount;
//showmessage(inttostr(length(memo1.text)));
//UniQuery1.Close;    {如果字段内容超长,并且没有检查的话,需要close再open,才能保证下一条合法记录的添加,否则会再次报内容超长}
//UniQuery1.open;
UniQuery1.Append;
UniQuery1.Fields.FieldByName('id').AsInteger:=GetMaxID1;
UniQuery1.Fields.FieldByName('name').AsString:=Memo1.text;
UniQuery1.Fields.FieldByName('text').AsString:=Memo2.Text;
UniQuery1.Fields.FieldByName('datetime').AsDateTime:=Now;
UniQuery1.Post;
//UniQuery1.Transaction.Commit; {由于UniQuery是自动提交事务的,所以这句不需要}
iend:=GetTickCount;
caption:=floattostr((iend-istart)/1000);
showmessage(UniQuery1.Fields.FieldByName('text').AsString);
end;
 
View Code

 

转载于:https://www.cnblogs.com/blogpro/p/11345673.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值