找回密码
 注册
搜索
查看: 5264|回复: 0

[收藏]VC++ADO连接数据库

[复制链接]
发表于 2005-9-1 23:30:26 | 显示全部楼层 |阅读模式
  一、ADO简介
1 y& n8 Q  a! I  `3 _" qADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。 3 y& Q/ V1 B8 @9 p. f) H
本文示例代码
) [' r3 C# Y3 y
( C$ V/ z4 k; H4 K* ^二、基本流程 9 z5 M4 V! {  M$ L
万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧! 6 d  p; H3 `: @) B9 j, Y
(1)初始化COM库,引入ADO库定义文件 * L1 ^9 Y1 s/ }. r& S
(2)用Connection对象连接数据库 . G# m* `; x# A
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。
% i% R) O( t% n" d, ^1 D9 Q(4)使用完毕后关闭连接释放对象。
* x, x6 ]' b( u! c5 s9 `1 s( X9 Z3 l
准备工作: # @$ C' F8 g% V8 E- b& ?
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。
! g( \2 @' U! H9 W下面我们将详细介绍上述步骤并给出相关代码。 / T- |) z+ Z& ^- O8 C8 ^% `
【1】COM库的初始化 , h" z4 a) ?* P9 Q
我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码: " R! g& m: f. ^% T* ^: m& M

, {/ ~7 p0 D# l) P9 R
& P% W9 u  e5 R. z7 |( j6 ?BOOL CADOTest1App::InitInstance()
* m/ q: ]- |0 a' X  {
8 `( G- i; B0 F% j( [7 q7 E  AfxOleInit(); ' ~* M: h8 I3 F4 [( g; [4 @
  ......
7 y2 l0 J8 }8 u5 V
  a: U/ `8 m4 t) K$ g& Y, [【2】用#import指令引入ADO类型库
- D6 G% p4 E  C% y我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到)
" J) b- C5 H3 ]  E#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
7 g9 S+ }/ v) g6 _) m$ V这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。 , }1 r5 L. ~* H' u2 V/ o$ [" s
& l4 Y6 J. {$ F6 Y- K
几点说明: 4 c, x+ z/ d# o* A7 w
(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改 : ?& A# p2 B8 b" Y
(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。 % ~0 P4 H; T9 S& _) A  D" v
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
6 @2 E& G/ q1 \5 J6 D$ F/ j8 T5 J/ ?, V- V9 {' h
【3】创建Connection对象并连接数据库 / g" O& j2 J* h  `# _! Z
首先我们需要添加一个指向Connection对象的指针:
" e& o0 o3 i- z! y_ConnectionPtr m_pConnection; " V' w6 \/ H. k7 F: b/ _, m% G
下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。 - y% @3 o+ Z  E. V$ q7 O

+ c' H* k  v% {6 ^9 R$ A" A, B' }# A! w- u5 o! H
BOOL CADOTest1Dlg::OnInitDialog()
8 {4 M; S3 {7 [  { / S! B6 V0 P( c$ V5 D' ?: L5 X# B
  CDialog::OnInitDialog(); ! V2 i6 o" U0 l7 }
  HRESULT hr;
( G; w( `8 ]2 X* Y% |' [  try 1 S* o0 l% D* m! K% v
  { ; X2 y+ x; ]8 u
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 " x1 z. r2 [  |
  if(SUCCEEDED(hr)) 1 u9 t0 y. b  |* o1 f
  { $ }1 n; E( k  i/ `4 |3 L
  hr = m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库 1 u9 a1 D0 c4 d5 l5 V
  ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为rovider=Microsoft.Jet.OLEDB.3.51;  } $ b6 f" @, i3 ^4 i
  } ) Q# |: b' c, P+ J! O
  catch(_com_error e)///捕捉异常   [1 c1 T0 ]/ l5 y: w, ?+ [3 F
  { , n7 R7 E0 ]- Q5 L0 s/ F
  CString errormessage; , a8 t6 x# e0 b9 l  i
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
6 g$ ?! G* `$ Q: u9 x; h, G2 e8 p1 C" z  AfxMessageBox(errormessage);///显示错误信息 2 W: ~2 N1 S+ w8 \. N
  } # T* a4 t# t) d& v' R
! C7 H2 ^1 W6 ]; N% |# I9 F# ~
在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型
. z. v' @6 S# T5 _! K; p/ u: @) BHRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options ) ' O4 x+ d  D; Q: V3 h" E* o# b
ConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权, ( X: n9 v. g+ y5 ]# n5 U
Options可以是如下几个常量:
- f$ G" a6 A: E* Z8 J* c3 fadModeUnknown:缺省。当前的许可权未设置
& w+ \2 p* r! B; ]. o" H% f' O! gadModeRead:只读 & j6 V4 g' N6 Z: e+ U" |4 k0 o# N
adModeWrite:只写
- }# ?1 S2 S% ^* @, i; ^adModeReadWrite:可以读写
# f) Q1 _% q5 n% cadModeShareDenyRead:阻止其它Connection对象以读权限打开连接 " Y* {4 M% q8 }+ X
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接
" R; r0 ~- s0 z# WadModeShareExclusive:阻止其它Connection对象打开连接 , ?2 s/ J: n! o7 R" z
adModeShareDenyNone:允许其它程序或对象以任何权限建立连接
: \0 |( y  w  G1 E' Q6 J$ v  g
我们给出一些常用的连接方式供大家参考: 7 d, F6 s2 |% z4 t# ?# i. ~
(1)通过JET数据库引擎对ACCESS2000数据库的连接 . ~% g# A& v5 q/ O' e# G3 V5 c1 b
  k: k1 g5 o2 e! I4 w
m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown);
8 M# F) m4 T" r( R1 C; {. ^0 ^7 F/ p8 g* E8 _6 @0 n$ o
(2)通过DSN数据源对任何支持ODBC的数据库进行连接: & c7 o- G# Q# s& a
m_pConnection->Open("Data Source=adotest;UID=saWD=;","","",adModeUnknown); $ X8 a) P8 |2 W, V

, @) \! x% N+ e(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown); 1 }1 H, B6 |- Q) `8 }6 e6 R

3 @( f% S* j% }" x! w1 E其中Server是SQL服务器的名称,DATABASE是库的名称
, \. ~& L; U, q  R
9 A3 O. y* b: V5 CConnection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State
' d, g7 Y$ V' f7 \( n! r- zConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
7 c7 S+ i8 @# q3 ~4 C+ Tm_pConnection->Open("Data Source=adotest;","","",adModeUnknown);
6 ~9 C9 j* P1 o: k  l
' F0 y4 a0 U( J# Y3 w
; \  \) x' l7 v# `8 _State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:
9 u+ K/ Q+ v- f, nif(m_pConnection->State) - Z# L( q2 x- |5 O' `. L/ W+ d
    m_pConnection->Close(); ///如果已经打开了连接则关闭它
) @& a7 M" i. ?' x+ z: _$ C/ e; Y! P5 N9 ^0 M

/ g9 e* B: ?# y8 H( Q: _【4】执行SQL命令并取得结果记录集
) G& I9 o4 b: i为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
2 [" T: W" K2 I+ w. H0 w并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
: H! b; c) y6 [6 f, R0 KSQL命令的执行可以采用多种形式,下面我们一进行阐述。
. I: c- n  W  ]$ |( ^; g; E; D1 I) u9 ?, \1 E: |( ~
(1)利用Connection对象的Execute方法执行SQL命令
" g; }+ b. H) Z, v9 c+ S1 S9 ?4 K, {Execute方法的原型如下所示: - W) [& M( C- i7 c
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一:
* L" p+ F+ U8 d7 h3 tadCmdText:表明CommandText是文本命令
9 z1 Q/ c; ]. T9 Y0 @) q& a- vadCmdTable:表明CommandText是一个表名   \  |+ @- P, a( L
adCmdProc:表明CommandText是一个存储过程
8 X/ e, g% u6 }0 m& MadCmdUnknown:未知 8 O* D: N- Q( p  R

) L+ d/ ^0 R! L( {Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。   _variant_t RecordsAffected; " T! M1 z6 P: i/ A) A+ G
  ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday
2 s! e, J" A5 p  T3 A2 c* b- W9 t  m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText); 3 S% \+ y. S; u
  ///往表格里面添加记录 3 p( Y3 [& w+ x( q) O( v# H
  m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnullnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText); / ?/ z, O6 u% P7 n! v
  ///将所有记录old字段的值加一
, S5 ?6 X; I7 j( @9 |  m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
# u* o% d6 N' P3 n  y  ///执行SQL统计命令得到包含记录条数的记录集 ( ~  D6 r% b3 }/ g4 G, W! @
  m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); . O8 O9 x( V; z" j0 N; |
  _variant_t vIndex = (long)0; ' ~/ A" s+ A+ f! \& |( u
  _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
0 J! B9 F8 Q1 K. h* k; L  m_pRecordset->Close();///关闭记录集 1 t" x" R; M, p1 Y
  CString message;
* T" j# {' W. \4 r: q, a. m  x" [/ z  message.Format("共有%d条记录",vCount.lVal);
: T: ?# l% ~3 p9 j$ ?. a  AfxMessageBox(message);///显示当前记录条数
" Y" l5 B( M- I. {9 ~" j
8 h2 s1 p2 s0 }; w( i& G9 ^5 B# ?1 Y1 O7 L7 Y; ]1 P
(2)利用Command对象来执行SQL命令
1 {+ F: v6 w; F) o  _CommandPtr m_pCommand; 8 k4 b; }8 ]" }% [& R* }
  m_pCommand.CreateInstance("ADODB.Command"); 8 I/ G- X. [3 t- a* L" S3 ?0 e
  _variant_t vNULL; - G6 s' m1 t/ r# i/ E3 {8 H
  vNULL.vt = VT_ERROR;
3 ], X; S' f4 A/ Z7 e! j5 d+ l8 t  vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
1 u* |8 y/ D/ Q) Q# K* Z  m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它 2 Q* c* ?2 U; Z( K6 c  s
  m_pCommand->CommandText = "SELECT * FROM users";///命令字串 ( N6 V9 ]- c$ e5 N2 P+ |; z
  m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集
3 b: z, W  V/ t. F5 R# _$ C
, K  T5 e. Y' @- L! D& s, [1 N6 X在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。 ' r& I( G# o8 S' O+ H3 [: `
+ F  t7 H/ _' u* f7 d( C2 ?  D5 E

& ]0 X. F% b2 e1 [# D9 {(3)直接用Recordset对象进行查询取得记录集
! j$ P4 b  U5 h4 h例如
( _( E5 A. H) [7 o( O6 b4 K6 c# G) p  G
  m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); / z" y+ t* f$ Z$ B9 U9 H3 I
, n* e2 I! x* ^& _$ L8 s% [" l
Open方法的原型是这样的: : _( |! f4 x! @/ w, |4 F4 k) O4 w
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options ) ' L. Q( |3 X3 x
其中:
( s! b, c9 m3 k; t- c①Source是数据查询字符串
( S' r, K" w, i  `, Q! E②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)
+ ^" Y; O, B# i3 D; q; |③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
2 `7 a; ~3 g+ O4 N$ ]3 T1 uenum CursorTypeEnum 6 L. g) ^9 d0 |8 ]9 q6 [
{
9 V4 |4 Q3 o3 r) [# U8 L6 @& D. ]adOpenUnspecified = -1,///不作特别指定 9 {& g2 r, ~9 U7 Q4 O, x/ V
adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
1 e5 F$ Q$ d7 b- @2 }; |adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。 9 }5 W2 `) A/ T8 I5 x$ }- a  A
adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。 % ]6 B2 E( ~( h& ~( H4 D" V
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。 9 ?  J/ X5 i  @* a; K
};
1 N8 Y, |2 i# j# |; A' v④LockType锁定类型,它可以是以下值之一,请看如下枚举结构: / p$ T# o- U& z' e- d$ D
enum LockTypeEnum
- ~' `& a+ C# `3 P- _! C0 `{ * ~/ ]. a0 I: e/ {2 m/ o9 W
adLockUnspecified = -1,///未指定
- `9 Y2 n* L0 z9 E3 N, V) oadLockReadOnly = 1,///只读记录集 * y* I1 X- ^  M) ]
adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制 ( @9 ?! ~5 P4 m8 E; i9 \! L
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作
1 F9 g5 t& H. `1 X6 B6 Q1 b8 badLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。 . n; t/ Q% v5 T) T& O1 c; R4 s
};
! ]! h' T2 r! {⑤Options请参考本文中对Connection对象的Execute方法的介绍
6 E/ q0 x& R+ G
0 j- w5 m: r6 B# I  U3 I$ |, m$ H- Z* V" H/ T5 u
【5】记录集的遍历、更新 8 f: K8 W* |; j" u! O! X. z: T
根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday * N. U  e6 Z/ ^
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。
0 p3 g  _4 _9 b9 ]( e( _
9 k/ j- R, \+ ^! }0 @% M# R0 N( ?8 h5 |# ~, Z8 K: p
_variant_t vUsername,vBirthday,vID,vOld;
, ^5 \" r! J, ^_RecordsetPtr m_pRecordset;
: L5 H+ C. I( |, {( qm_pRecordset.CreateInstance("ADODB.Recordset"); 5 m! \5 N* w! o! t- J8 w  z
m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); 3 p7 P! c1 s1 z: `" q" C0 v
while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗?
3 P' P2 R0 f  w+ Q{ 6 n# ^" U( M- b3 J. C
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,你也可以直接给出列的名称,如下一行   v) r7 ~5 d( W8 ?& A
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
  y) }% I7 a5 ~. B, C' X4 uvOld = m_pRecordset->GetCollect("old");
. i6 B1 j& a2 I: O& [/ v2 evBirthday = m_pRecordset->GetCollect("birthday");
% c0 z$ J0 a  e( n, w( h* W4 q///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
  H7 }8 Y: N3 U" `if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL) + \- D8 j" Q- z  @# L7 q
  TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday); ) w! Q+ K9 c2 r; ?
m_pRecordset->MoveNext();///移到下一条记录
) I# x, T$ ~9 r$ @& n}
* B, [* M# L  O5 O- Zm_pRecordset->MoveFirst();///移到首条记录 9 K; C9 W; X" Q' P) V+ Y
m_pRecordset->Delete(adAffectCurrent);///删除当前记录
9 X0 N- |/ T* f/ C3 ]% |7 Q& [///添加三条新记录并赋值
' b  }9 B- z9 h% i8 dfor(int i=0;i<3;i++)
8 i+ R2 Z/ h- j. r4 B{
3 e* |/ I; w4 E0 Ym_pRecordset->AddNew();///添加新记录 $ s& B6 G) }9 K( u
m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
% F2 A: w0 `+ j* G  nm_pRecordset->PutCollect("username",_variant_t("叶利钦"));
& d! F% \, P7 J0 G9 }: A6 }m_pRecordset->PutCollect("old",_variant_t((long)71)); & U# Q% P- A( f' }" B* o
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));
# o6 F2 T7 ]9 R* T' _! s% ]} 9 d3 m% F( s  P0 v
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
0 W( Y% e/ k' n1 }# l7 \; F: im_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄 * K* |1 f+ N1 `  n
m_pRecordset->Update();///保存到库中
+ l; u! `) T9 t) b9 P4 ~1 y1 ]* w7 V$ Z. e( n# L7 u3 e
【6】关闭记录集与连接 . @( }5 p& B# U! ^
记录集或连接都可以用Close方法来关闭 * }; l2 R& a# t5 h, [
     m_pRecordset->Close();///关闭记录集
- n+ D6 O  ~7 G' X6 I! i      m_pConnection->Close();///关闭连接
( F( J2 ]. O2 c+ v9 C8 k) s7 Y7 O0 D* _6 q  h) R
至此,我想您已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子中读出所有记录放到列表控件中、并可以添加、删除、修改记录。 * @: k7 G! d# X  G9 E
点这里下载示例代码
' d8 E+ v/ p; W) F4 S
4 g6 ^( W0 e# O" B后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-6-20 00:50 , Processed in 0.036934 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表