delphi 在 Delphi 中使用原生 ADO 控制数据库 |
| 来源:站内 关于:bill 发布时间:2007-06-27
|
[ 收藏]
[ 推荐] |
|
我发现很多朋友在开发数据库时都使用 Delphi 自带的 ADO 组件 或 Diamond ADO,其实在 Delphi 中使用原生 ADO 接口也是十分方便和有效的。我使用原生 ADO 开发项目已有很长一段时间,也回答过一些朋友类似的问题,现在把自己的一点心得与大家分享,班门弄斧,只是希望能对大家有所帮助。当然,这帖子也是原生的,不是转贴的。
一、优点 1、大家知道 Delphi 对 ADO 接口进行了一番包装后形成了 ADOExpress,我想 Borland 的主要目的还是想与自己的数据敏感控件相连。然而事实上数据敏感控件并不是那么耀眼,如果你希望编出来的程序稍微有点水准的话就别用那玩意;如果你很少使用数据敏感控件,那么 ADOExpress 基本上失去了其应有的作用,无数冗余的属性、虚方法,不管你用不用得到一股脑给你编译进去,也会使你的程序再大上 200K;效率么,不说了。 2、MSDN 和 VB 中的例子你可以搬过来就用。 3、关于代码重用:我给大家的例子都是以函数或过程形式,重用性不好么? 4、别说帖子太长,那你看看 DB.pas, ADODB.pas 多长?
二、基本储备 1、一些必须的单元 uses Variants, ComObj;
2、一些基本常数(其它查 ADODB2000.pas 或搜索 adovbs.inc): const adOpenDynamic = $00000002; adOpenStatic = $00000003;
adLockOptimistic = $00000003; adLockBatchOptimistic = $00000004;
adConnectUnspecified = $FFFFFFFF; adAsyncConnect = $00000010;
adStateClosed = $00000000; adStateOpen = $00000001; adStateConnecting = $00000002; adStateExecuting = $00000004; adStateFetching = $00000008;
adUseServer = $00000002; adUseClient = $00000003;
adModeReadWrite = $00000003;
adXactCursorStability = $00001000;
adCmdText = $00000001; adCmdTable = $00000002; adCmdStoredProc = $00000004; adCmdFile = $00000100;
adAffectCurrent = $00000001; adAffectGroup = $00000002; adAffectAll = $00000003; adAffectAllChapters = $00000004;
adEmpty = $00000000; //空 adInteger = $00000003; //长整 adSingle = $00000004; //单精 adDouble = $00000005; //双精 adCurrency = $00000006; //货币 adGUID = $00000048; //全球唯一的标志码 adDate = $00000007; //日期 adDBDate = $00000085; //日期值(yyyymmdd) adDBTime = $00000086; //时间值(hhmmss) adDBTimeStamp = $00000087; //日期时间值(yyyymmdd hhmmss) adBoolean = $0000000B; //布尔 adVarChar = $000000C8; //字符串 adVarWChar = $000000CA; //文本 adLongVarWChar = $000000CB; //备注
//存储过程参数结构,只定义了常用的部分,如果需要自己加吧 type TParameter_ = record Name: WideString; Type_: LongWord; Direction: LongWord; Size: Longint; Value: OleVariant; end;
TParameters_ = array of TParameter_;
3、一些基本函数和过程 //创建 Connection 对象 function CreateConnection: OleVariant; //释放 Connection 对象;cnn 为 Connection 对象 procedure FreeConnection(var cnn: OleVariant); //创建 Recordset 对象 function CreateRecordset: OleVariant; //释放 Recordset 对象;rst 为 Recordset 对象 procedure FreeRecordset(var rst: OleVariant); //创建 Command 对象 function CreateCommand: OleVariant; //释放 Command 对象;cmd 为 Command 对象 procedure FreeCommand(var cmd: OleVariant); //用 Connection 连接到 SQLServer 数据库;cnn 为 Connection 对象,srv 主机名或 IP 地址,uid 用户名,pwd 密码,db 数据库名 function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean; //执行 SQL 语句,有返回行,无事务处理;cnn 为 Connection 对象,rst 为 Recordset 对象,sql 为 SQL 语句(可以是存储过程) function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean; //执行 SQL 语句,无返回行,有事务处理;cnn 为 Connection 对象,cmd 为 Command 对象,sql 为 SQL 语句(可以是存储过程) function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean; //执行存储过程;cnn 为 Connection 对象,cmd 为 Command 对象,rst 为 Recordset 对象(用于接收来自存储过程的数据集);prc 为存储过程名; prms 为参数集合 function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean;
function CreateConnection: OleVariant; begin try Result := CreateOleObject('ADODB.Connection'); Result.CursorLocation := adUseServer; Result.IsolationLevel := adXactCursorStability; Result.Mode := adModeReadWrite; Result.Provider := 'SQLOLEDB.1'; except if not VarIsEmpty(Result) then Result := Unassigned; end; end;
procedure FreeConnection(var cnn: OleVariant); begin if not VarIsEmpty(cnn) then begin if cnn.State <> adStateClosed then cnn.Close; cnn := Unassigned; end; end;
function CreateRecordset: OleVariant; begin try Result := CreateOleObject('ADODB.Recordset'); Result.CacheSize := 1000; Result.CursorType := adOpenStatic; Result.CursorLocation := adUseServer; Result.LockType := adLockOptimistic; except if not VarIsEmpty(Result) then Result := Unassigned; end; end;
procedure FreeRecordset(var rst: OleVariant); begin FreeConnection(rst); end;
function CreateCommand: OleVariant; begin try Result := CreateOleObject('ADODB.Command'); Result.CommandType := adCmdText; Result.CommandTimeout := 5; except if not VarIsEmpty(Result) then Result := Unassigned; end; end;
procedure FreeCommand(var cmd: OleVariant); begin if not VarIsEmpty(cmd) then cmd := Unassigned; end;
function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean; begin Result := not VarIsEmpty(cnn); if Result then begin if cnn.State <> adStateClosed then cnn.Close; cnn.ConnectionString := 'Provider=SQLOLEDB.1;Persist Security Info=True;Initial Catalog=' + db + ';Data Source=' + srv + ';Connect Timeout=10;' + 'Use Procedure for Prepare=1'; try cnn.Open(cnn.ConnectionString, uid, pwd, adConnectUnspecified); except Result := False; end; end; end;
function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean; begin Result := not (VarIsEmpty(cnn) or VarIsEmpty(rst)) and (cnn.State = adStateOpen); if Result then begin if rst.State <> adStateClosed then rst.Close; try rst.Open(sql, cnn, adOpenStatic, adLockOptimistic, adCmdText); except Result := False; end; end; end;
function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean; begin Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen); if Result then begin cnn.BeginTrans; try cmd.ActiveConnection := cnn; cmd.CommandText := sql; cmd.Prepared := True; cmd.Execute; cnn.CommitTrans; except cnn.RollbackTrans; Result := False; end; end; end;
function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean; var i: Longint; begin Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen); if Result then begin try cmd.CommandText := prc; cmd.CommandType := adCmdStoredProc; cmd.ActiveConnection := cnn;
//之前,将我们自己的参数集合中的输入参数添入 Command 参数集合 for i := Low(prms) to High(prms) do case prms[i].Direction of //未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数 adParamInput, adParamInputOutput: cmd.Parameters[prms[i].Name].Value := prms[i].Value; end;
rst := cmd.Execute;
//之后,将输出参数从 Command 参数集合读取到我们自己的参数集合 for i := Low(prms) to High(prms) do case prms[i].Direction of //未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数 adParamOutput, adParamInputOutput: prms[i].Value := cmd.Parameters[prms[i].Name].Value; adParamReturnValue: prms[i].Value := cmd.Parameters[0].Value; end; except Result := False; end; end; end;
三、访问数据 1、最前 rst.MoveFirst; 2、最后 rst.MoveLast; 3、向前 rst.MovePrevious; 4、向后 rst.MoveNext; 5、取当前记录 rst.Fields[0].Value 或 rst.Fields['字段名'].Value; 6、修改当前记录 rst.Update(rst.Fields[0].Name, 某值); 7、取消修改 rst.CancelUpdate; 8、删除当前记录 rst.Delete(adAffectCurrent); 9、删除所有记录 rst.Delete(adAffectAll); 10、追加记录 rst.AddNew; rst.Fields[0].Value := 值1; rst.Fields[1].Value := 值2; rst.Update; 11、刷新 rst.Refresh; 12、记录数 rst.RecordCount 15、其它方法和属性查 MSDN 或 ADO 的帮助;
四、一些例子
//变量声明 var cnn, rst, cmd: OleVariant;
//创建对象 procedure TForm1.FormCreate(Sender: TObject); begin cnn := CreateConnection; rst := CreateRecordset; cmd := CreateCommand; end;
//释放对象 procedure TForm1.FormDestroy(Sender: TObject); begin FreeCommand(cmd); FreeRecordset(rst); FreeConnection(cnn); end;
//连接数据库 procedure TForm1.Button1Click(Sender: TObject); begin if ConnectToDB(cnn, '127.0.0.1', 'sa', 'ok', 'Northwind') then Caption := '连接成功' else Caption := '连接失败'; end;
//取记录 procedure TForm1.Button2Click(Sender: TObject); begin if ExecSQL(cnn, rst, 'SELECT * FROM Products') then Caption := VarToStr(rst.Fields['ProductName'].Value); end;
//执行存储过程 (* CREATE PROCEDURE GetProductName @ProductID int, @ProductName varchar(50) OUTPUT AS SELECT @ProductName = ProductName FROM Products WHERE ProductID = @ProductID RETURN @ProductID *) procedure TForm1.Button3Click(Sender: TObject); var prms: TParameters_; begin SetLength(prms, 3); //参数 0 接收返回值;Name 取什么都行,但 Direction 必须标明为adParamReturnValue prms[0].Name := '@RETURN_VALUE'; prms[0].Direction := adParamReturnValue; //参数 1 作为输入参数,Name 必须与实际参数名相同,Direction 标明 adParamInput prms[1].Name := '@ProductID'; prms[1].Direction := adParamInput; prms[1].Value := 8; //参数 2 作为输出参数,Name 必须与实际参数名相同,Direction 标明 adParamOutput prms[2].Name := '@ProductName'; prms[2].Direction := adParamOutput; //执行存储过程;如果不需要 Recordset 对象接收返回的数据集,就把它置空 ExecProc(cnn, cmd, Null, 'GetProductName', prms); ShowMessage(prms[0].Value); ShowMessage(prms[2].Value); end;
五、原生 ADO 与 Delphi ADOExpress 组件的对应关系 1、Connection <=> ADOConnection.ConnectionObject; 2、Recordset <=> ADODataSet.Recordset; 3、Command <=> ADOCommand.CommandObject; 4、? <=> ADOQuery,因为 ADOQuery 根本就不是原生 ADO 对象 5、ExecSQL <=> ADODataSet.Open; 6、ExecSQLA <=> ADOCommand.Execute; 7、有了上面几个其它的就不多说了
六、与数据库结构有关的一些函数 1、动态改变字段名称 uses ComObj; //Access //TableName: 表名; OldColName: 原字段名; NewColName: 新字段名; procedure RenameField(const TableName, OldColName, NewColName: string); var cat, col: OleVariant; begin cat := CreateOleObject('ADOX.Catalog'); cat.ActiveConnection := ADOConnection1.ConnectionObject; col := CreateOleObject('ADOX.Column'); col := DB.Tables[TableName].Columns[OldColName]; col.Name := NewColName; end;
//SQLServer procedure RenameField(const TableName, OldColName, NewColName: string); begin with ADOCommand1 do begin CommandText := 'EXEC sp_rename ''' + TableName + '.' + OldColName + ''',''' + NewColName + ''',''COLUMN'';'; Excute; end; end;
2、取得 Access 库中的表结构 type TTableDef = record Name, DateCreated, LastUpdated, Description: string; end;
TTableDefs = array of TTableDef;
procedure GetTableDefs(const DBName: string; out TableDefs: TTableDefs); var dbEngine, db: OleVariant; i: Longint; begin try dbEngine := CreateOleObject('DAO.DBEngine.36'); db := dbEngine.OpenDatabase(DBName); SetLength(TableDefs, Longint(db.TableDefs.Count)); for i := Low(TableDefs) to High(TableDefs) do begin TableDefs[i].Name := db.TableDefs[i].Name; TableDefs[i].DateCreated := db.TableDefs[i].DateCreated; TableDefs[i].LastUpdated := db.TableDefs[i].LastUpdated; try TableDefs[i].Description := db.TableDefs[i].Properties['Description'].Value; except TableDefs[i].Description := ''; end; end; finally db := Unassigned; dbEngine := Unassigned; end; end;
3、取得 Access 表中的字段结构 type TFieldDef = record Name: string; Types, Size: Longint; Description: string; end;
TFieldDefs = array of TFieldDef;
procedure GetFieldDefs(const DBName, TableName: string; out FieldDefs: TFieldDefs); var dbEngine, db: OleVariant; i: Longint; begin try dbEngine := CreateOleObject('DAO.DBEngine.36'); db := dbEngine.OpenDatabase(DBName); SetLength(FieldDefs, Longint(db.TableDefs[TableName].Fields.Count)); for i := Low(FieldDefs) to High(FieldDefs) do begin FieldDefs[i].Name := db.TableDefs[TableName].Fields[i].Name; FieldDefs[i].Types := db.TableDefs[TableName].Fields[i].Type; FieldDefs[i].Size := db.TableDefs[TableName].Fields[i].Size; try FieldDefs[i].Description := db.TableDefs[TableName].Fields[i].Properties['Description'].Value; except FieldDefs[i].Description := ''; end; end; finally db := Unassigned; dbEngine := Unassigned; end; end;
4、不用 SQL 语句,仅用 ADOX,创建 Access 数据库、表、字段、字段备注(同样适用于 SQLServer) function CreateMDB(const DBName, TableName: string): Boolean; var cat, tbl, col: OleVariant; begin Result := True; try try //生成数据库 cat := CreateOleObject('ADOX.Catalog'); cat.Create('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + DBName);
//生成表 tbl := CreateOleObject('ADOX.Table'); tbl.ParentCatalog := cat; tbl.Name := TableName;
//生成一个带备注、自动编号的长整字段 col := CreateOleObject('ADOX.Column'); col.ParentCatalog := cat; col.Name := '带自动编号的长整字段'; col.Type := adInteger; col.DefinedSize := 4; //自动编号字段就是长整字段,仅仅多了下面这句话 col.Properties['Autoincrement'].Value := True; col.Properties['Description'].Value := '自动编号长整字段的说明'; tbl.Columns.Append(col, adEmpty, 0); col := Unassigned; //生成一个带备注的文本字段 col := CreateOleObject('ADOX.Column'); col.ParentCatalog := cat; col.Name := '文本字段'; col.Type := adVarWChar; col.DefinedSize := 50; col.Properties['Description'].Value := '文本字段的说明'; tbl.Columns.Append(col, adEmpty, 0); //生成一个不带备注的双精字段,比上面要简单的多 tbl.Columns.Append('双精字段', adDouble, 8); //生成一个不带备注的备注字段 tbl.Columns.Append('备注字段', adLongVarWChar, 16);
cat.Tables.Append(tbl); except Result := False; end; finally cat := Unassigned; tbl := Unassigned; col := Unassigned; end; end;
七、进行分页显示数据 俺们几乎每天都见到网页用 ADO 进行分页(每次只取一定数量的记录),但是在 Delphi 中分页也成了稀罕的东西 :( 分页有三个常用的属性:PageSize—页面大小,PageCount—页面总数,AbsolutePage—决定跳转到哪一页面。 1、首先取得数据集,不罗嗦了 ADODataSet1.Open.... 2、下面这个例子,将指定页面的数据读到 Memo1 中 procedure GetPageData(const nPage: Integer); var i: Integer; begin wiht ADODataSet1.Recordset do begin if (State = adStateClosed) or (nPage < 1) or (nPage > PageCount) then Exit; Memo1.Clear; AbsolutePage := nPage; //这里只能用 for 不能用 while(Eof 仍然表示数据集的结尾) for i := 1 to PageSize do begin Memo1.Lines.Add(Fields['ProductName'].Value); MoveNext; //防止因最后一页未满而产生越界 if Eof then Exit; end; end; end;
八、其它 1、希望此贴能对朋友们有所帮助,我写的这些都是经过实战检验的; 2、觉得有用的朋友可以试一下,如果觉得没用就当我没写,耽误你时间了。 |
[浏览:
次]
|
| 上一篇:delphi 利用线程FTP上传上个目录指定的后缀文件 下一篇:delphi 如何使用API
|
|
| |
[ 收藏]
[ 推荐]
[返回顶部] [打印本页] [关闭窗口] |
评论加载中…
|
|
|
|
|
 |
google adsense热点文章 |
|
|
|
|
|
|