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;
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;
對於單數據集的提交:
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;
--無返回值的插入數據

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');
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';就執行報錯
基於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.
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.
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;
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;
對於單數據集的提交:
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;
直連模式(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;
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.
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.
三、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;
需要注意的是,必須先判斷連接控件 (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.
有網友問我常用的控件及功能。我先大概整理一下,以后會在文章里面碰到時再仔細介紹。
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)));
測試加密,解密正常。
用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;