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

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

[复制链接]
发表于 2005-9-1 23:30:26 | 显示全部楼层 |阅读模式
  一、ADO简介 9 R/ a& v/ W9 }, y
ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。
4 r4 ^+ {" o/ [* K& N本文示例代码
& X- R7 S: I9 g- H' F+ y+ N' F% |$ X* S! u5 R+ V
二、基本流程 ) q$ R) `- S/ m" Z- U) m
万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧!
4 b9 _/ }2 W/ P3 e; U+ C(1)初始化COM库,引入ADO库定义文件
! ]% }7 N2 A9 k& e7 t; R& ^(2)用Connection对象连接数据库 % ^' y- K' D3 [0 o0 u
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。 6 P! O. s/ ~1 P3 `5 E' ~
(4)使用完毕后关闭连接释放对象。
+ _0 a) W" w- X* q4 `, w( D& Z' t+ Z# g8 s, o4 O
准备工作: 0 u' |* z% F) x( y' e! R
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。
6 g9 G8 F. _* s% ^- A, v: Q下面我们将详细介绍上述步骤并给出相关代码。
. l& Z+ u: P4 Y: b) t* N【1】COM库的初始化 1 S5 o/ C( C7 J" p* N5 V3 R* t
我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码: # P2 X9 c/ g: Z6 h0 `) C' B

% p9 f6 D- P2 t; ^* R- J) h4 P! Q) j4 S$ O
BOOL CADOTest1App::InitInstance()
- Y. {) E: W4 [% L  { - S' [) I* T2 Z; ?3 H
  AfxOleInit();
, e1 B1 s# E3 ~; }3 |  ......
! k/ v" u4 H: W' M. T* k
- I& @. @6 c- ?& g【2】用#import指令引入ADO类型库
9 i$ e1 Z- M" ]4 i9 ?0 C! _, p) Z我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到) % G" A  q, e  u9 g
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
% x. ~! ], R' O8 o/ V这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。 . ~+ h: r9 S5 O; \, O- N1 l

; {1 ]* X- A0 W( ^$ J几点说明:
- H( I8 ~) e1 @1 x- l9 t(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改
6 k/ B# m9 N0 n3 I# i  |(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。 3 [# S- q' q" A, ?- M
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
3 I" ]; ^/ g  @- s) z
% Y5 q, s+ n' I3 N  e+ u【3】创建Connection对象并连接数据库 - s6 w! F# }5 D. H9 W) U8 q0 A
首先我们需要添加一个指向Connection对象的指针: * g' E0 @9 Y8 d" S! n
_ConnectionPtr m_pConnection;
: {: q& {* P3 u  y下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。
- c3 A9 c! c; O& p8 O( R& z$ g6 `0 g: B4 R) }
8 p. F3 J' y, C! R1 Z
BOOL CADOTest1Dlg::OnInitDialog() , T/ j/ \  r6 d! L5 T
  { & e- v' _7 a& {! O5 l
  CDialog::OnInitDialog(); " B( E' Q5 x7 j- g2 T8 P- p( j
  HRESULT hr;
+ _% q* U/ u9 ]/ i  try
) H. w  n$ H. v, c- a5 A  { . p, ^- X7 @3 ]
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 $ h. x- v# e, h) M) F3 E
  if(SUCCEEDED(hr)) ) ^% s/ K3 j9 N" p8 y& Y
  {
/ h( V+ X7 W9 D  hr = m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库 3 \6 P& |( d) h' K
  ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为rovider=Microsoft.Jet.OLEDB.3.51;  }
9 L' r/ |, M/ ~  p' o7 u  } 0 c9 q! U# O. e% `
  catch(_com_error e)///捕捉异常 ' y: }* s8 \0 ^, E8 C
  {
" T* q7 T: B; Q; a$ q$ B  CString errormessage;
: _9 E/ S% g0 p& u% \( }% U! p  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage()); 1 m# S9 X) a% S7 {4 H
  AfxMessageBox(errormessage);///显示错误信息
4 z7 E# q& z1 U! i: c% g' E5 l  }
0 }! d4 f+ y: V6 a
( R* D, Z: ^6 q( E: v/ T# s2 h2 W在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型
6 ]  E# l, v, G7 FHRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options )
6 g: W% N: \3 X+ DConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权,
, J. S$ v  d6 O& F8 Z! ~( o$ P( eOptions可以是如下几个常量:
9 L4 `6 @' f5 Y& Q: aadModeUnknown:缺省。当前的许可权未设置 4 F/ ^1 Z- o3 j  Q% {
adModeRead:只读
1 |# W8 U2 W# @1 S& I4 vadModeWrite:只写 3 w8 T' C) Q; d, I) r$ w! L* |/ \
adModeReadWrite:可以读写 " }4 f! b( V3 p& y/ Y& Q
adModeShareDenyRead:阻止其它Connection对象以读权限打开连接 % K0 r0 @7 Z' @8 K8 t0 y$ X
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接
( F) G% ~" U# N) ]! A$ ?3 UadModeShareExclusive:阻止其它Connection对象打开连接 1 r0 x2 a' `+ J5 B$ W
adModeShareDenyNone:允许其它程序或对象以任何权限建立连接
! \4 n0 T8 C4 n, b
% M; _2 N0 {/ i" g# B我们给出一些常用的连接方式供大家参考:
" r" D1 p- \" N& Z% d6 D- n9 y(1)通过JET数据库引擎对ACCESS2000数据库的连接
' A# J& ~( G/ l' j
& [% K! a) b$ a) w* l$ _8 S+ `m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown); & @* n% h1 a9 e' O  n: g

% Q" i& V, C' c$ r4 T; W(2)通过DSN数据源对任何支持ODBC的数据库进行连接:
7 u9 b- ^4 Z5 P; f' y$ w- am_pConnection->Open("Data Source=adotest;UID=saWD=;","","",adModeUnknown);
' A- G: T' \2 O$ C# T$ Z! Y: i' }9 r' S* n
(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown); " i3 Z- x  O: W' E5 h
  c' E' o* S5 z, x7 K. ^# U- y% u
其中Server是SQL服务器的名称,DATABASE是库的名称   x' g! Z% M. B9 a/ h- O
; u& C/ r% p1 ]# @
Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State ( a. r6 L0 e4 v! |; C7 `
ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒 + w' j. r: {3 y4 I
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown); " I0 G. _+ r) z) U0 l0 {5 j
& d! [) N. F$ f& x1 |
: x2 d. ?+ S5 _
State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如: ; S. a1 N3 w5 `% H
if(m_pConnection->State) . z% @* y9 W0 h0 j
    m_pConnection->Close(); ///如果已经打开了连接则关闭它
6 P+ ?; s0 `2 v  C- c# f( D) `9 I* B  l: g# k/ A* Y

$ h) V) l' ]+ I  ~5 L  X& \& J8 B【4】执行SQL命令并取得结果记录集 1 j2 ]# {- U% \) \& ?
为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
/ P$ m: w; _9 o$ o- d并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset"); % u$ ?- K9 u6 s1 _1 {- Q5 }
SQL命令的执行可以采用多种形式,下面我们一进行阐述。 4 x1 r+ g1 O  H2 S7 s
, ~. a( P8 a4 P5 z
(1)利用Connection对象的Execute方法执行SQL命令 / A% Z, {& n4 B2 ]
Execute方法的原型如下所示: 3 h  _: ?: A6 |) F
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一:
( P1 K) ~, i7 {% E- \& A5 SadCmdText:表明CommandText是文本命令 ( r# j* g9 G+ g* s* L8 q
adCmdTable:表明CommandText是一个表名 0 P5 G/ F4 q& d7 V6 m( b
adCmdProc:表明CommandText是一个存储过程
; G6 ~/ Q( T- e, GadCmdUnknown:未知 : P% f: [, V( i, U8 `

  _, ~6 G+ Z; D5 s9 mExecute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。   _variant_t RecordsAffected; $ V6 r5 r) R# z0 _) v4 n
  ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday ; K$ Y5 J) A6 u% V& I
  m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText);   z' J( F+ c: x2 F
  ///往表格里面添加记录
2 [2 t7 g' S% d& o  H# |, W! u3 ^  m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnullnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText); 7 r1 d" O$ d2 \
  ///将所有记录old字段的值加一 + p1 K( }$ e  E5 Z# \5 J  n- W; C
  m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); , O0 R4 |( y7 B5 x3 t: S7 e
  ///执行SQL统计命令得到包含记录条数的记录集 : Q. v' ?/ `. Z7 s
  m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); , X7 R; T1 _6 ~- i) X% t0 R
  _variant_t vIndex = (long)0;
3 X) e& \4 Q8 |3 W. R( q( P  _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
% H' @4 b  Y! E5 y! t3 Z( q* n: S  g  m_pRecordset->Close();///关闭记录集 , n. n3 N! a6 g" G/ Q1 C* }0 o
  CString message;
! G# C: K9 ^! l$ x  message.Format("共有%d条记录",vCount.lVal);
  ^0 b3 C2 {! Y% d7 T( j/ x  AfxMessageBox(message);///显示当前记录条数 2 t; \7 q4 W- @: t
1 ~$ V: G/ G9 y% S2 S! v5 j, s

# U( k, l7 b, M6 M: b(2)利用Command对象来执行SQL命令
( M# k( l( N. n8 @  A  _CommandPtr m_pCommand; , t# c: o9 I2 [* N% g
  m_pCommand.CreateInstance("ADODB.Command"); # ~9 X+ Q7 }7 R7 s6 e4 l
  _variant_t vNULL;
" n2 n) e+ ]* P  vNULL.vt = VT_ERROR;
. ^: A0 P% O. z; y  vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数 " E( B  o3 x, _0 o# m! p% r7 t8 m
  m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它
5 t+ \2 K6 _0 {/ r# q0 p' Y  m_pCommand->CommandText = "SELECT * FROM users";///命令字串
' G% T2 c3 a6 t2 n' P7 B+ Y% q  m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集 2 {5 U( x( y' g3 N

% i1 k) U0 q) `在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。
  b- V2 L9 V- f' f/ t- h6 `
% p; k, `% h* r2 T' o' A$ b- F9 w/ e- P0 k; U# F9 O; C; m
(3)直接用Recordset对象进行查询取得记录集 4 t' D; V. H" N
例如 , l% u" h$ T! f! S( B- D5 R8 m

' d5 L% }4 C. m2 i' W  m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); ! r4 M+ ], @. H7 D' x% _% \! k: E
: }/ l& T6 v: u2 G* q8 x
Open方法的原型是这样的:
7 n( N/ `" b( c/ h3 JHRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options ) 4 m! O9 x# `& P
其中:
: u: E8 u/ p/ c7 D% @$ |①Source是数据查询字符串 + B7 V+ y, Y( b; d" J  l& |
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) 7 q2 V3 o8 T& x* j8 w2 K
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
* G: K- v1 y, fenum CursorTypeEnum
  C8 Q+ X- d( S; ~{
5 T* c2 h9 L2 kadOpenUnspecified = -1,///不作特别指定
1 {5 S1 E, O$ W) f7 kadOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用 ; r' X7 s1 R$ T" f. h% Y
adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
0 A6 Y! J* S; A6 R* JadOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。
8 O  B7 g1 p0 }adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
- q/ |2 g! f% _6 G: ~6 E( F}; ! {3 p1 F$ ^" C' N
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
9 E9 V$ ?' r; fenum LockTypeEnum
( S; K, _6 ^. [1 t5 a: ]{ : e& X' \( c: K2 g3 {+ f( O
adLockUnspecified = -1,///未指定 & a. n$ V; c8 {* X6 w: H& ]7 g
adLockReadOnly = 1,///只读记录集
; y' B, P* w+ h. k  D# o: {adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制 $ Z7 `2 L8 Y7 r0 e5 ^
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作 ) m2 s* W( D  `0 P9 {; L% q2 R
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。 8 g( Z: u3 `2 N1 b+ F
}; ! K; H. M3 ?; S3 j2 l
⑤Options请参考本文中对Connection对象的Execute方法的介绍 4 G5 b. L8 U; z% K- o' s

1 _7 |% B9 ?6 |; ]# e3 ~* [+ s( X, b7 i2 [" j% {3 z1 m
【5】记录集的遍历、更新 # X8 A8 h) ?% D3 y6 @9 c4 ~6 d, t
根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday 2 c0 d8 O) T) S) }
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。 0 m, _1 y  z6 w7 V
; e( v3 ~' v* A" Q% V2 G# W

8 E7 q# Z& I, d" C5 x_variant_t vUsername,vBirthday,vID,vOld; * g) w/ t; F" c$ D
_RecordsetPtr m_pRecordset; ! `5 ^* }. J" N/ T4 K" I
m_pRecordset.CreateInstance("ADODB.Recordset");
7 s. d1 C8 L! h! U+ C6 R' X0 r0 [m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);   b( K2 Q- v1 G, i/ u6 R9 f
while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗? , j7 `. l, b$ [% j( n6 _& f
{ ; u, |& N+ N; }) j
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,你也可以直接给出列的名称,如下一行 + N7 J( K# C7 `! C9 g" D
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
* Z4 R" j8 q$ ^, A) {6 m& z& cvOld = m_pRecordset->GetCollect("old");
) q& }0 I& _, w% x5 PvBirthday = m_pRecordset->GetCollect("birthday"); 6 \! ?! R3 S, }  m
///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
3 T' o7 v; K& u$ M$ D5 ?if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
8 s, q: N! m, y5 t3 c$ q& C: H' d  TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);
" ~  J4 @2 V- y+ S) L! r- cm_pRecordset->MoveNext();///移到下一条记录
: p% k, Q; O9 O8 `" V) `}
" `8 ^, ^% G' r, l- Rm_pRecordset->MoveFirst();///移到首条记录 4 ~$ G+ B! V) Z( t4 ]
m_pRecordset->Delete(adAffectCurrent);///删除当前记录 1 o  @; c. e  h0 z0 o' w
///添加三条新记录并赋值 , i6 c; Y& Q/ `  f3 k9 M
for(int i=0;i<3;i++) % K4 {) J& c$ F( k& w& g
{
, y, b  q2 T- qm_pRecordset->AddNew();///添加新记录 ) Q! k' Y0 L8 N/ H! g
m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
6 D$ P; K, X) f2 W$ b4 A3 z( j( Om_pRecordset->PutCollect("username",_variant_t("叶利钦"));
& f7 u! ?: z3 Mm_pRecordset->PutCollect("old",_variant_t((long)71));
9 e! Y; N. d" M5 ~% ym_pRecordset->PutCollect("birthday",_variant_t("1930-3-15")); + k; T9 ~2 T- F) V: z; q& x% v
} 9 V6 u) {& G5 D0 q
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
5 T2 Q# @6 f. p) s1 Zm_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄
8 z' u6 h/ V( ]8 n- c; f, e5 Y8 b+ H  ~m_pRecordset->Update();///保存到库中
" r% k) l1 \  X# v
1 P9 P, z0 I, ^3 G2 g' a) d【6】关闭记录集与连接 ' s, o( G0 _$ _& }5 \. p) S5 p/ A. u
记录集或连接都可以用Close方法来关闭
$ k: x( A0 b# z0 q      m_pRecordset->Close();///关闭记录集
; m1 S* Z( m  l( W$ v; {. Q      m_pConnection->Close();///关闭连接
" y; r, {% P. t$ `& X) |$ d# s2 e. M- n4 Q+ g
至此,我想您已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子中读出所有记录放到列表控件中、并可以添加、删除、修改记录。 * d% ^0 \% ^' Z: A% [' s: Q" O5 q
点这里下载示例代码
7 I2 h  r' \$ `0 v" B& Z
$ F# K- f* v1 Y( N: z' L后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-14 00:06 , Processed in 0.019067 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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