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

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

[复制链接]
发表于 2005-9-1 23:30:26 | 显示全部楼层 |阅读模式
  一、ADO简介
# D5 e3 M0 ?& \& wADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。 9 C# f4 I2 P, z' g: ]% q
本文示例代码 7 m. J+ h0 u0 z! S: X
: \; u( L5 V1 [9 M8 c4 U
二、基本流程
5 n8 `. a& X2 c( o& E/ C+ ~! X万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧! 5 `- w/ x! O: S" q5 }: D
(1)初始化COM库,引入ADO库定义文件
( x: k( k1 F3 Y2 s( }(2)用Connection对象连接数据库 ( }0 X& W$ l( Y# X% g4 r
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。
7 c3 Q" d) j2 ?5 X5 \& i/ x7 N(4)使用完毕后关闭连接释放对象。
+ m- `1 T' D. J5 j: B6 v" L! ~9 I" @9 ]9 f% K
准备工作: # y+ o% p- S. A% U1 \5 ~9 {
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。
4 ^( @  q4 \$ \6 O( {下面我们将详细介绍上述步骤并给出相关代码。
+ ^: Z2 A# ~% p【1】COM库的初始化
& C' T, g: R4 o% f2 [2 i3 C我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:
. N% j* J0 D  g: _0 @1 @1 r5 k% H! F3 k

8 y' }8 a# u, q9 r, C/ _BOOL CADOTest1App::InitInstance() ! c" H8 X2 ?% T" o' b$ Z( v) u# d
  {
( f+ y6 L: F% v( c" b  AfxOleInit();
* k3 i/ [" Y- ~2 ?  ...... 8 q7 v7 }8 i5 d- u
+ ^! i% B3 \3 B' k. ?( |
【2】用#import指令引入ADO类型库 - m7 q# ?' n. T* S
我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到) " d" f' \% a* ]. i
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
2 h3 `9 t" J5 p! R+ r这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。 % _3 O0 t" N) a- ]2 O+ y
( x) s5 ?9 [& |8 }+ V
几点说明:
# y4 _0 n8 x* ?(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改 2 v( C9 V' F4 J! a
(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。 / g8 i6 y4 E4 D6 f) _  M
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
: X* J$ d/ ]" i, B1 V
1 {% ^* ]" e% i) I3 Z【3】创建Connection对象并连接数据库 1 f# ]4 I/ |6 H7 M" h3 o
首先我们需要添加一个指向Connection对象的指针:   x4 @! B! j6 a# X! O- z
_ConnectionPtr m_pConnection;
1 B6 ^0 ]7 G( x下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。 2 F. l  ?- _5 L1 w; m/ Y- o. s4 @+ `
0 \& t9 a8 \+ f5 d. g) x+ w

; @5 }/ d/ `* }1 g2 MBOOL CADOTest1Dlg::OnInitDialog() 3 k. f$ W- T* y/ `7 |8 P
  { , N" S3 I6 N0 C8 @" z& o' d  @1 V' H2 P
  CDialog::OnInitDialog(); ! e- X4 x- M; e) Z: k/ V
  HRESULT hr; * ?3 R. N9 I: R# T7 l1 J1 K7 a
  try
0 V0 O5 k- {# b9 c  { : f9 O: n0 O0 v- V* P! |& _9 B3 [
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 ! R9 L( ^/ _7 o" o
  if(SUCCEEDED(hr))
- M% r1 Z2 x6 ~0 O6 X  {
% W5 N& l* ?2 n5 u4 u  hr = m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库 4 [! v) }0 q% B! S  l  r
  ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为rovider=Microsoft.Jet.OLEDB.3.51;  } # P% l8 P; p# t, p+ P5 X# D" i
  } ( @, ]' ^3 a7 F; [
  catch(_com_error e)///捕捉异常 / p6 S% e) S, P7 `7 m& }7 H
  {
4 f0 ~: t9 z. s# j: T2 b8 d  CString errormessage; 9 i) p4 I( l- [& y7 c9 u6 A
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  n% `: F; `0 ?$ ]& c% u  AfxMessageBox(errormessage);///显示错误信息
1 k% a$ T4 S8 L, @  } " q5 @4 L1 w0 ]; |" }, I

$ S1 r& t$ g% q在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型 - u$ M* o* A) y7 V' I" }- `8 }
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options ) 6 i! z/ p; ~0 }* _- {0 e7 k
ConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权, 3 f, O. P. K4 K) w+ U
Options可以是如下几个常量:
7 P5 U" S+ Y0 v  z# t/ }adModeUnknown:缺省。当前的许可权未设置
! D9 Z; \8 G, m" o5 n3 UadModeRead:只读 2 p% H8 \$ r8 d- w5 j: L
adModeWrite:只写
  s' F6 m& {! b) XadModeReadWrite:可以读写
1 \. }3 e4 H8 S* m, ?; OadModeShareDenyRead:阻止其它Connection对象以读权限打开连接 4 j2 R; R' D# k6 [  [( R
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接 - S. `$ h" F9 [: ]) f+ c
adModeShareExclusive:阻止其它Connection对象打开连接
# c0 v0 j7 W+ e3 TadModeShareDenyNone:允许其它程序或对象以任何权限建立连接
  S( E; {; k4 z& \
9 d9 j# `6 u6 {6 ~; x: n. ^* n- j' v# E我们给出一些常用的连接方式供大家参考:
; T, }9 R5 t: R0 d, t+ m8 a(1)通过JET数据库引擎对ACCESS2000数据库的连接 / o' T* [0 Z2 b

! d6 I9 b$ w8 W# r8 Om_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown);
0 ^9 S2 a6 M/ ~* @
2 _* X. q! W2 W& G; Y9 X(2)通过DSN数据源对任何支持ODBC的数据库进行连接: 2 x1 p/ a# k. K. P- D2 ~
m_pConnection->Open("Data Source=adotest;UID=saWD=;","","",adModeUnknown);
- z. F. u* o- |: L* o: ?
3 \1 |6 {8 f/ P(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown);
9 q$ _) l! J) i- z" _
  y* ^/ J0 r$ S3 n. F9 F/ e其中Server是SQL服务器的名称,DATABASE是库的名称
) `+ R; p- @6 e% }) H1 w+ ^: g% A4 d9 V: G
Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State & _2 D" n2 R% F5 L
ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
8 u4 r* i* h2 e3 ?  f7 im_pConnection->Open("Data Source=adotest;","","",adModeUnknown); ; Z1 w$ w1 j! t! r
6 a% C2 H: T! {8 y# R2 Z

: q  n. H1 w+ n4 Y. a- U% MState属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:
+ u4 w( Q! M3 d% `, v, Bif(m_pConnection->State)
7 ^0 {3 f. I" d$ c$ _     m_pConnection->Close(); ///如果已经打开了连接则关闭它 * U, ?9 w; x: X) _" c

5 P9 H6 K/ z) [3 u- E
' u* b' a9 u' _: K6 g0 Z【4】执行SQL命令并取得结果记录集 , ]7 i2 ]' O7 }3 G* k  A, H/ _
为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;   h% r; {6 W. S/ e- u6 \
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset"); 2 a( p1 P" `0 y* P/ F: x' ~: J) }
SQL命令的执行可以采用多种形式,下面我们一进行阐述。
. q1 r8 S; [0 E$ t/ `1 l; \- S! A" j3 v
(1)利用Connection对象的Execute方法执行SQL命令
3 a' `, _* z7 X. z, g+ O( TExecute方法的原型如下所示:
# {7 j6 n7 Q+ o; [/ @$ l: J: |! }_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一:
2 X+ r$ q. p9 x2 Z; dadCmdText:表明CommandText是文本命令
' o: N6 P9 b3 XadCmdTable:表明CommandText是一个表名 8 L) H$ V) @# f. W3 z
adCmdProc:表明CommandText是一个存储过程 $ p3 S. o  R7 a! Y
adCmdUnknown:未知 # F# l; `. ^: E
; D1 X3 [. T4 V6 V* `( D5 R
Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。   _variant_t RecordsAffected; # w( j! R4 P& c5 c
  ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday 5 y6 X7 q( ?  T* p/ F
  m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText);
. {! k( ~# N. ]6 _- N  ///往表格里面添加记录 5 v4 g# N8 l, d+ ?7 P
  m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnullnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText); ' q5 i  J! S, [- y& |) o( y& e
  ///将所有记录old字段的值加一
, p+ M' C2 [; U% n4 ?  m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); ' c) t. N5 w3 y7 n' b
  ///执行SQL统计命令得到包含记录条数的记录集
5 L" \+ z  }# J/ b0 c  m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); 6 t- [( @; B. S
  _variant_t vIndex = (long)0;
2 E- |" a  N! X& F; |  _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
' @2 ]  v! L" ^' h  m_pRecordset->Close();///关闭记录集
5 N% i, h% p/ l, @8 D6 }6 |2 f3 ~  CString message; - d( P0 |- i" Y& U% ^
  message.Format("共有%d条记录",vCount.lVal); ) ~- g1 ]( v1 p
  AfxMessageBox(message);///显示当前记录条数
! A* x% Y' p( C0 f6 [. X0 v/ x4 d$ p1 x; E, D6 F5 h

/ S7 o; U# a" |+ ]8 y: N8 p(2)利用Command对象来执行SQL命令
$ e$ N! ?3 P; o0 u  _CommandPtr m_pCommand; % _+ z( w4 [2 k: ?4 G, \3 `3 v
  m_pCommand.CreateInstance("ADODB.Command"); 9 k3 L/ U& Q  Y
  _variant_t vNULL; 0 i( D/ z. I6 t* W& {
  vNULL.vt = VT_ERROR; ( X2 T! ~0 @1 z5 M4 N
  vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数 * a" ~% K1 M7 {5 U/ B! c
  m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它 $ s- _- U' r  [, g. _7 n$ T
  m_pCommand->CommandText = "SELECT * FROM users";///命令字串 ; a) N! U9 R4 F* d$ D) ]+ q
  m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集
2 M7 E4 }1 f. d8 X) S! m4 M. K. X& i- ^9 x1 P% p" x
在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。 % m  S5 _3 \' a- j( U
, H! C! u: F' n$ @% q4 J

# k* n! L# X- ~8 G! [' s+ m, B$ t(3)直接用Recordset对象进行查询取得记录集
, B* {6 q" }7 {例如 3 u, e" F4 K  v

* T6 X8 p; B& W' o1 M9 _  m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); ; g# [$ i( K7 U; e- K" ?
0 `) U8 @& b2 \) n+ W
Open方法的原型是这样的: 3 J5 F2 G  r1 I$ k+ h) T2 V$ J
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )
- @$ F) @: R5 E% b其中:
4 L" c$ T5 [. [①Source是数据查询字符串 7 E" m( z0 W4 y) {0 I6 @
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) ; a; l( h) @' g9 f) R" i  L/ J/ H
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
0 T) N9 A6 `/ z( i7 R6 Renum CursorTypeEnum + A. C5 B: _: c. W
{
8 m' y/ {) s, d* h8 E; EadOpenUnspecified = -1,///不作特别指定 # _) Y* f/ J" j' K4 p8 ?
adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
- E( c1 p9 t9 d2 _adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
8 c& m8 S# y* T3 kadOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。 # V& c4 {" F; s" L. X6 \: F8 r
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
8 w8 D2 K: q7 L- ^8 D, M};
, a; }- o1 C% U  Z- d④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
6 Z7 G( u  i  G2 T0 Oenum LockTypeEnum
4 l1 c6 W- d7 ]1 L{ ! O3 N3 m* ?. n7 b9 }) _. `7 C
adLockUnspecified = -1,///未指定
( o9 m6 @! _5 w5 N8 y: r5 hadLockReadOnly = 1,///只读记录集 7 ^) M4 }& [0 d* `% t( L0 h! G5 W
adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制
2 o8 [! ?8 R! P! d  ]adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作 - _3 B3 ?4 x  `9 u! j' |4 t  h! }
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。 ) q/ z# Q' n) R1 U& T+ T
}; 0 q7 i, x% i( R0 i% g) S
⑤Options请参考本文中对Connection对象的Execute方法的介绍
: p- u# F* T- e; {- J( L
' i- E! A- \# q5 T$ N$ P& z1 Y" l# M: r
【5】记录集的遍历、更新   W2 W9 \, D- A( C0 Y
根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday 6 F+ Y9 J3 [9 B$ [( w& |0 u
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。 1 Y; _# l+ M! M7 c, K" _7 v2 X
' f7 D, {. Y6 ]! i
0 R# _: c& T2 S0 y0 P4 r) Z
_variant_t vUsername,vBirthday,vID,vOld; 4 s2 {' P" b5 J. p. ^( `
_RecordsetPtr m_pRecordset; 7 X. E# S! L, r; a
m_pRecordset.CreateInstance("ADODB.Recordset");
) l3 ?; r% _: C6 L6 n: M7 u% hm_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); 4 A! C5 ^3 m$ ]4 S3 W; v1 d) r
while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗? $ C! [1 Y9 q* A, |: H; E
{
. c$ f- _; c5 vvID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,你也可以直接给出列的名称,如下一行 : a% y3 O! V" ~. A6 w7 s3 J
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
, D8 c4 b: J% x+ l1 k2 c; M, A" gvOld = m_pRecordset->GetCollect("old");
, h2 R0 _) i1 p; a; j/ JvBirthday = m_pRecordset->GetCollect("birthday");   o& ]8 J1 s& |
///在DEBUG方式下的OUTPUT窗口输出记录集中的记录 9 l  b; r+ n8 [
if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
2 P/ k+ |5 O8 K) L  b  TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);
9 |. E" }; x, U& Z& p6 u3 S3 sm_pRecordset->MoveNext();///移到下一条记录
9 }9 S0 B  [  R}
, j. a% T9 E6 @; y3 ?4 M, w, N" lm_pRecordset->MoveFirst();///移到首条记录 9 e( I! ?. h8 c6 \
m_pRecordset->Delete(adAffectCurrent);///删除当前记录
7 e: A# K' N9 b/ w2 B///添加三条新记录并赋值
! b6 m5 v3 l0 }% J* Kfor(int i=0;i<3;i++)
" y" P" _  \& z& B9 x{
3 ?- j* e* @0 ~0 Q5 D6 Em_pRecordset->AddNew();///添加新记录
" ~5 A8 ^: G( [m_pRecordset->PutCollect("ID",_variant_t((long)(i+10))); # F+ c6 o+ ^( i3 O6 b
m_pRecordset->PutCollect("username",_variant_t("叶利钦"));
  q3 B5 b' n: {0 u$ gm_pRecordset->PutCollect("old",_variant_t((long)71)); 0 ~! C: v0 t# q: \' v9 s+ _% B
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15")); ' ~2 @& \" n0 B- p
}
1 f/ C9 `+ W: Bm_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
! F- a9 V% L( a# r* }; N6 em_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄 4 [- X9 G7 c2 F7 G: z
m_pRecordset->Update();///保存到库中
! n  W) i# P9 A3 }6 [8 W. h2 W
6 t. @- Y) l* |; R6 U( i【6】关闭记录集与连接 ) O7 @8 \7 N8 w# Q, |" X" L
记录集或连接都可以用Close方法来关闭 5 O! E$ ?/ b- T  b6 E, m; A/ |
     m_pRecordset->Close();///关闭记录集 2 d# |* M, P; J4 n7 }" v) \
     m_pConnection->Close();///关闭连接 # Z1 Z: ~2 x! I' c' G
  H: ~# u' O$ S/ [' I) ^
至此,我想您已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子中读出所有记录放到列表控件中、并可以添加、删除、修改记录。 & t$ E1 G( z; l1 R9 ~2 e
点这里下载示例代码 ) h2 B9 z0 v' \9 }

" h% N  O+ r& A7 e- e5 c( b: k( l. Q后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-5-2 08:58 , Processed in 0.019025 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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