找回密码
 注册
查看: 5741|回复: 0

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

[复制链接]
发表于 2005-9-1 23:30:26 | 显示全部楼层 |阅读模式
  一、ADO简介 5 s0 \  i3 R7 C4 B
ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。
# g2 [# e! }! u) k; O本文示例代码
2 A3 |4 n  s/ b/ [
# F- |4 A! y  k, J" P) N" `% ^3 D  R二、基本流程
( H( \$ U+ o+ p6 F) w万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧!
, B$ b; J& v2 B& r% o4 i( [3 c(1)初始化COM库,引入ADO库定义文件
2 Y/ B, K& A: y7 j) l8 \(2)用Connection对象连接数据库
3 v- r/ b. E0 D# k(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。
2 A3 e) _& |- s" K0 k(4)使用完毕后关闭连接释放对象。
1 T0 U/ K0 n% l6 [* l' H7 Z8 Q5 z6 p
准备工作: # }  c/ k8 j$ M3 U- s
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。 % Y' L$ ^# _1 {+ T
下面我们将详细介绍上述步骤并给出相关代码。
! p, Y: T1 }$ W/ v) \2 X【1】COM库的初始化
& l5 M/ `" `- a  ?/ I* r我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:
4 ~8 X' ?0 S9 s6 P6 u+ b
! N0 X8 j+ C5 G4 a' a! r$ [0 c5 w9 l/ p, Q5 E( p: W
BOOL CADOTest1App::InitInstance() 0 F8 t# T& ?, P5 U. I) }
  {   @# a! S6 }0 t6 q2 n' \3 t0 [# W
  AfxOleInit(); 8 c/ M8 _+ H% A# Q
  ......
" _, W( Q+ k6 d# n& Z6 C' n% J. J- K
9 H  x$ H/ i9 ?- F【2】用#import指令引入ADO类型库
8 w7 {0 ~" }: H* h/ U我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到)
/ a0 p( y. f5 l# I) `$ T' ?  I#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
% x/ M2 e6 I/ x这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。
6 T% R% |1 O9 E: R. B
2 p* X6 @5 i- F) B0 Z9 Y几点说明: ; V: `# R+ B* ~/ Q. E. U- H1 J" ?/ ?
(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改 ' B# `0 n, G0 ^
(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。
( `, T( H9 a  C8 amsado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
( J: ?. e/ k) J8 }1 J7 u* e+ N) N; M
【3】创建Connection对象并连接数据库
- x& `1 _# a3 w首先我们需要添加一个指向Connection对象的指针: 6 t$ }5 P: {5 K! H9 s/ Q, G
_ConnectionPtr m_pConnection;
. X7 [3 ]" V0 {' c4 j8 g6 W) y下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。 7 R# ^2 `0 x" C( S

4 s& q. |" v2 F& O6 }) Y9 a0 }+ H7 }# F: D
BOOL CADOTest1Dlg::OnInitDialog() 2 J& e# M& D; A! t
  { - h( R9 S6 L0 k
  CDialog::OnInitDialog();
' }- B- Y3 x$ K% @( {9 Y  HRESULT hr; - h2 r; R. D5 Y' {: L
  try " x4 Z1 S* H8 E5 s( S
  { % w. k/ C) C0 y# d
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 0 W" b8 {% V0 p2 H2 L
  if(SUCCEEDED(hr))
2 G9 y& }1 D/ p/ [" \  {
0 f* `. H1 Q* d  hr = m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库
) [9 v2 t. M5 {& Z/ M. A6 Y  ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为rovider=Microsoft.Jet.OLEDB.3.51;  }
2 ~9 h. h+ X' g) j: w  } 6 A2 g+ W$ r& s1 L2 F
  catch(_com_error e)///捕捉异常
. d5 c, J% Z4 m  {
  W4 o8 a0 o( l1 `/ E6 E0 l  CString errormessage; " k0 N! T8 T0 ~% m/ A
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  _7 u1 e/ p# H7 ~% I, H  AfxMessageBox(errormessage);///显示错误信息
& J$ q% i7 U- N1 B5 {: O  } / o3 c, K1 s& _/ H# j) p' [

, O: x/ k% x0 m+ {6 K- s; r# ~. ]在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型 " Y  M3 G2 z) _7 ~
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options )
% U' ?' g9 J& V$ \' c  o9 C; |0 NConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权,
1 \7 L# h1 j: D" pOptions可以是如下几个常量:
; M. \7 N& ~1 Z7 VadModeUnknown:缺省。当前的许可权未设置
% ~4 i# B5 Z6 |- c) ^( P' |adModeRead:只读
8 K: s% t5 z* c9 D4 TadModeWrite:只写
, [3 i0 _7 k" A3 L" SadModeReadWrite:可以读写 & q! A9 \& K: \/ J/ G
adModeShareDenyRead:阻止其它Connection对象以读权限打开连接 + e( m3 T& E- m' \. w
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接   b1 Y. \/ l; u& w6 S
adModeShareExclusive:阻止其它Connection对象打开连接 4 ^9 [. ^3 \1 t& E, K" I9 A9 a# a
adModeShareDenyNone:允许其它程序或对象以任何权限建立连接
/ j! l$ q( k8 |/ @, P1 H  j  t& q: Y
我们给出一些常用的连接方式供大家参考: # Z9 ?2 E! E2 U* l& _
(1)通过JET数据库引擎对ACCESS2000数据库的连接
2 H$ t, b6 s1 r" j# a, |
2 _0 H5 w' Y! d( ~2 ^m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown); 1 N' v6 k( d0 V' ]- D$ S( q4 z

6 A! i) J3 A3 g, J6 Z; [% p(2)通过DSN数据源对任何支持ODBC的数据库进行连接: 4 [" ~7 \$ V6 L  P
m_pConnection->Open("Data Source=adotest;UID=saWD=;","","",adModeUnknown); % @! U7 b: h' n6 {

% l$ r) N- \4 z; G) A4 K+ M- X(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown); 7 l* O* j2 F) V" `0 m- V6 E7 H

. h8 o& U" F( ^6 ^3 D其中Server是SQL服务器的名称,DATABASE是库的名称 # I0 S+ R% ~: C- d* F
) V' l4 m; o. K( C4 N2 ^3 e# O
Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State 0 R6 g2 @9 z- f
ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
& N# x  \( u( Rm_pConnection->Open("Data Source=adotest;","","",adModeUnknown);
2 w8 X1 P$ R8 c/ Q* w3 a% p% `+ h
7 Z+ z) R2 G' E+ W# v" P9 m
State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如: 0 U4 a3 Q) F0 W0 o
if(m_pConnection->State)
0 \% t& [4 d1 I3 W3 D     m_pConnection->Close(); ///如果已经打开了连接则关闭它 : _" I6 C1 H, i0 S4 \4 H3 |
2 P( I* T2 ]$ I) F

. @" Y. L7 n9 ~; I  n' S【4】执行SQL命令并取得结果记录集 ; |4 Z. X6 G& |) ^* d+ Z! T
为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
4 f7 a( X" k5 P! d  h并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
" O- I  a- ]! M; j( y2 H- ^6 m# DSQL命令的执行可以采用多种形式,下面我们一进行阐述。
* l& p7 c) u% e  `' h" a# J$ L. O) R. @1 s
(1)利用Connection对象的Execute方法执行SQL命令
$ s" o( Z, a/ X/ O/ E* xExecute方法的原型如下所示:
+ f) N  U7 @+ j' j_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一: 0 l2 A! x6 V% P8 z8 O0 Q* h
adCmdText:表明CommandText是文本命令 : l! Y' d& n' w$ M
adCmdTable:表明CommandText是一个表名 1 K1 i4 k) {. A+ q; v
adCmdProc:表明CommandText是一个存储过程
" v' m' o9 M. `2 r1 VadCmdUnknown:未知 $ W4 o$ h" P  r9 f
: C5 I- \2 S& f% Y0 [
Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。   _variant_t RecordsAffected; ; d3 N- D: m' b6 ~3 b2 [
  ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday
% q$ k  G7 Q5 Z. Q  J6 b5 e  m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText);
0 k  q, ^2 ~7 _! a$ m7 E# s  ///往表格里面添加记录 & G6 z0 z; o3 S) K
  m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnullnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText); 8 c% e; U  r! y: }( \
  ///将所有记录old字段的值加一 : M0 L6 B* s& @, U4 e, l, b
  m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
  G$ j# Y3 f& U: D- p  ///执行SQL统计命令得到包含记录条数的记录集 * K4 y# Q- m# M& |  _# n' U
  m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); 0 V3 v, w- p, q* W( ?
  _variant_t vIndex = (long)0;
" R! d( H2 _, A$ h2 d  _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量 ! k6 u2 W; {* c
  m_pRecordset->Close();///关闭记录集
$ A/ V  m' p$ O, D/ Z% T. p  CString message;
+ ]0 I: {5 w  `: B* t) S4 t  message.Format("共有%d条记录",vCount.lVal);
6 o; S) \( R( ~4 E! l  AfxMessageBox(message);///显示当前记录条数
2 H/ w) u! s0 n' b1 g( f+ v8 j9 K0 w1 N, A# N
, }  o, @. ]! k- j* e
(2)利用Command对象来执行SQL命令 3 a/ ^; q# n3 ~
  _CommandPtr m_pCommand; ! F! f8 P% v4 ]& z
  m_pCommand.CreateInstance("ADODB.Command"); 9 e* {/ Y4 ~* T) u$ q0 \9 N5 r
  _variant_t vNULL;
" v2 f" Y' D) k& A  I, z  vNULL.vt = VT_ERROR; * C9 Q6 i. U* e. _+ W+ p  G
  vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
  |% `/ ]. H$ F; R& Y  m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它
$ [# z" J% z* x  m_pCommand->CommandText = "SELECT * FROM users";///命令字串 9 \! X  V* [3 x4 q+ A
  m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集 # _! W. D! `/ M$ o
# c; b5 v5 u2 o* u; ^
在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。 4 B. ~3 l  R/ O9 N1 W7 P+ Z: K/ v
- |1 ^4 M$ n6 x
% x5 A# a& t+ V9 K
(3)直接用Recordset对象进行查询取得记录集
$ v2 j5 S5 z/ ^' y5 r例如
7 K$ c/ ?( R8 l1 \2 Q1 f6 K, M/ i8 f& g' G
  m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); . A- v8 K  t- Q5 t. _

) r& b0 i; O7 l( oOpen方法的原型是这样的: - G! D$ |9 j9 I
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options ) - Z$ b9 E8 u3 k- F6 o2 r6 T
其中:
0 w3 c* R9 x5 m4 u  r* @①Source是数据查询字符串
' k/ |/ d/ [) [% B6 R, Y5 N6 B7 q②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) : @7 }% _) u& z3 ]! ?% }- r. c' P$ P
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
0 w+ Q# M9 Q. L/ W$ m  Uenum CursorTypeEnum
$ |- x6 W4 Y6 a0 S, p: D{
; p' H" @; h; m' E5 N  aadOpenUnspecified = -1,///不作特别指定
6 W) r: |: f3 M8 v" OadOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用 6 s- C7 i' _' Z* z. B
adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
% U, L( Q. l' y  }( \' A  Y6 badOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。 7 {2 W3 O  S# u* l; x/ b
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
6 n8 c$ o( P; j/ X}; 6 t5 J+ }/ ?- H; {2 q$ c' A2 p. j
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
/ e3 Z! S# M9 s3 u6 X0 i; Venum LockTypeEnum
; V; T0 p) M! _5 o0 n: @{
" {  y8 ?2 F8 L/ _adLockUnspecified = -1,///未指定
  j/ Z& r8 A/ b9 `adLockReadOnly = 1,///只读记录集
3 h! s" s$ c9 r: L: h: t& qadLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制 8 ~6 ?: \; V/ P5 _( r
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作 ' }: T& C6 ]( e
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。
- ]5 K! e# e% M8 q};
/ q# h8 G: N$ O% L⑤Options请参考本文中对Connection对象的Execute方法的介绍
; p' ^2 V1 ~% j8 U- N3 D. P: G
: Z* ~, Y" R" s
; B$ i/ k- D( v【5】记录集的遍历、更新
- o$ F( A" y9 a根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday
8 D- d% U' K! I4 ~9 |! M& k( v以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。 7 k" t2 Y1 B  }, n( m. ~- ?
: m3 H; h7 D. l8 L( _

8 e* ]( b% B6 U5 G$ F_variant_t vUsername,vBirthday,vID,vOld;
- a% F' J) C( [' W0 E_RecordsetPtr m_pRecordset;
5 S* q# p6 _, k0 @7 `+ J. Pm_pRecordset.CreateInstance("ADODB.Recordset");
8 N$ A! b5 Y* x' z2 xm_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
% }7 ?& F* [. Vwhile(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗?
7 `( ^8 G' I7 _7 n6 P* [{
( Q4 x7 `" O: q4 d& T+ z  xvID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,你也可以直接给出列的名称,如下一行
9 u- y# e/ X) W/ c( t) R( PvUsername = m_pRecordset->GetCollect("username");///取得username字段的值
  E4 A& D" m8 r3 s0 U7 F% @vOld = m_pRecordset->GetCollect("old"); $ Y9 M, @" J3 L! w
vBirthday = m_pRecordset->GetCollect("birthday");
% E3 E8 ?# ~* r5 Y///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
) d( k% V2 D; lif(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
, {: y0 Y6 a+ w. ^  TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday); 2 T% V6 _0 n# X( c, h: z) h
m_pRecordset->MoveNext();///移到下一条记录 8 c1 ^" m8 D! [6 S; `$ W/ k3 H
} , M* ]% _5 r* T' Y* p
m_pRecordset->MoveFirst();///移到首条记录 2 R7 c! i& p5 V: P4 M9 V. v
m_pRecordset->Delete(adAffectCurrent);///删除当前记录 % ]' V+ A2 N: b1 y$ _; @; Q0 \
///添加三条新记录并赋值
5 {5 s- R+ S, u7 G& h: }) i: rfor(int i=0;i<3;i++)
, m: n' @( L' M+ N$ I8 L8 ]{
9 z/ r) R2 p1 n( d1 \8 {* D4 Gm_pRecordset->AddNew();///添加新记录
1 n# [& A; C! d" gm_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
/ n/ z% b0 `& L2 Im_pRecordset->PutCollect("username",_variant_t("叶利钦"));
) c" P! R$ V& l4 d& D- Hm_pRecordset->PutCollect("old",_variant_t((long)71)); & G8 g# g+ ~! }, ~8 f, A6 V1 Z
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));
& ?( _1 E  l" z. N* s}
! j$ t1 {( _0 A4 ]$ X6 c) Ym_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处 3 L3 P& a* O7 z
m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄 ; X! ]* |; h5 j1 \7 b( P- }
m_pRecordset->Update();///保存到库中 7 e" D( j! A: U9 h: U8 z

5 E) [3 j  O3 P2 M& H【6】关闭记录集与连接
* Y+ o+ L4 h7 p记录集或连接都可以用Close方法来关闭
1 w# |9 F8 {4 J- v. y# }      m_pRecordset->Close();///关闭记录集
7 z: O5 Z# D' m; T8 A, m$ ]# L/ ]      m_pConnection->Close();///关闭连接
3 C/ d: g& H' f3 p0 |: Y. g9 y
. l9 K- N4 ^0 f2 P至此,我想您已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子中读出所有记录放到列表控件中、并可以添加、删除、修改记录。 6 t( }, q  F$ {; W, ]' J
点这里下载示例代码 0 f3 j6 Z: y8 o8 k
$ l: u+ w0 p* e% B. @& o9 x& N
后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-10-2 05:03 , Processed in 0.016983 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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