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

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

[复制链接]
发表于 2005-9-1 23:30:26 | 显示全部楼层 |阅读模式
  一、ADO简介 " j) y" t4 @! I# z# p9 _
ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。 * h4 p" r1 n' W( n. ]2 G+ q
本文示例代码 & J9 Z' h- J5 g) |8 z' c7 S( Q

! ?( |% s; c/ O+ \% L; g二、基本流程 8 J, _& C! i( u) R% K
万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧! + k7 ^- }! n+ d- ], ~$ F: s
(1)初始化COM库,引入ADO库定义文件 * F4 P+ D, n0 Y! I' t
(2)用Connection对象连接数据库
, N2 F5 K# U8 d' c. b8 V(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。 - d! ?4 |6 Z# i( x
(4)使用完毕后关闭连接释放对象。 , O1 g; g2 m* W  R9 y
# `, t9 H. z4 a) T
准备工作: + U1 \! t. r5 A; D6 z$ _
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。
4 c! R) E( f/ X8 b下面我们将详细介绍上述步骤并给出相关代码。
* v, j6 m: n% z$ J2 G' q【1】COM库的初始化 5 g4 R; T0 ?# {- u. U# Z
我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:
: }" A5 G% m' c5 m% C6 s) u8 Q7 t% M6 I" U+ y

, a1 y0 q" t" P  c  |. BBOOL CADOTest1App::InitInstance()
) j) Y0 g' D2 }4 T/ H/ Y  {
. S% y1 D7 E5 B' F. e  AfxOleInit(); % G" p5 S( p' T4 Z' n8 j- V
  ...... % K; {1 y/ X! e& ?. n, Z) u# C
6 ?  y7 c; y1 k; x
【2】用#import指令引入ADO类型库 . i6 k! V  Q0 a6 x
我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到)
: |0 k# ~' n# |#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF") + v& ?" L5 D3 I9 G) |) ?
这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。
# r( l5 m3 E4 Z9 o3 r% N  z  l( t1 ?8 U8 Y
几点说明: , [8 o: V. G7 U$ s. o$ x. T
(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改
. r/ J  S$ x( s- o9 x3 R* V) X(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。 7 C/ G4 O* n1 O6 ~( u' Z- _
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned 6 }( M/ ~( O. m8 p. L" C0 q  U
( h2 a' t4 ^& G" P) E$ g/ T
【3】创建Connection对象并连接数据库 " h1 G$ f4 d* N. B) |5 j
首先我们需要添加一个指向Connection对象的指针: 6 Z4 ^2 k3 v9 ?
_ConnectionPtr m_pConnection; $ n7 f! n3 B. j7 Z' @, B
下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。 : M$ H8 t' |2 N
- U  B  F" a( p

9 ?* u  w8 Y4 J: Q5 Y1 nBOOL CADOTest1Dlg::OnInitDialog()
# j8 e9 }4 D  n- v: t: c+ K' L, V7 A  { # r, C/ k# J8 z2 C5 Y: c
  CDialog::OnInitDialog(); % }9 F* i1 a/ m- c' g' H, [, J
  HRESULT hr; : l+ l* D( v) ^1 a, z( v, E$ C
  try - U: \9 `# l2 C5 B+ J% ?4 y' C1 |. b0 _
  { : Z- ^, U! b5 [1 f
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 ) `/ F1 S  O9 m; D( _9 \* j
  if(SUCCEEDED(hr))
. H/ G8 G( Z9 X! k  {
' O; y$ n( Z, K4 S0 @1 D  hr = m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库 4 o: l: P& h2 Q* T2 B% s& ]' q
  ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为rovider=Microsoft.Jet.OLEDB.3.51;  } . X  v7 \, v4 q: E$ \
  } & k5 @. l4 E' t3 Q* |
  catch(_com_error e)///捕捉异常 1 A3 k& l6 p9 [4 c9 {4 |
  {   g: C0 d. G8 t$ P# ^- `
  CString errormessage; " \! t( L- o3 E
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
0 H9 u& o  t- P# E6 F; P  AfxMessageBox(errormessage);///显示错误信息
  C7 m. Q/ v& Q: k  } 9 r* J1 B3 e5 n6 ~
& h) f2 R& r; x
在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型 ( X* K2 C! E8 ]8 q7 n
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options )
0 q) f# h* l, v( v: e" E. z% B8 LConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权,
5 R) T& R$ h$ _8 d9 N/ @# I, yOptions可以是如下几个常量:
! ~" V0 \1 r! o, k5 zadModeUnknown:缺省。当前的许可权未设置 $ l  @" X  }; v, G0 \% c
adModeRead:只读
8 D) d; e1 e  V% Y: `4 L+ s* k6 I1 radModeWrite:只写 % w( j; u! d7 H
adModeReadWrite:可以读写
- @$ z7 W% c! z- Y6 e) e+ c! i2 xadModeShareDenyRead:阻止其它Connection对象以读权限打开连接 ( d3 y' ^; y; F  W% W( g3 `4 j
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接 7 B5 Z$ J$ [) m5 w! ^
adModeShareExclusive:阻止其它Connection对象打开连接
6 N1 M8 X) P9 P& x- [. _: z8 kadModeShareDenyNone:允许其它程序或对象以任何权限建立连接
6 R9 t  |5 e1 N! ~! B
; P. N& o; s& A: p- W) C我们给出一些常用的连接方式供大家参考: . j# r3 T+ I; y& K: e3 k- D" z; R
(1)通过JET数据库引擎对ACCESS2000数据库的连接 4 p3 c7 H! g, k- x
2 p, S% c7 c) c, G: R
m_pConnection->Open(&quotrovider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown); * ?9 c! M+ d0 r" i$ ~

* x7 y& B  G" g9 j; \1 T" T(2)通过DSN数据源对任何支持ODBC的数据库进行连接:
. X0 z! k: ?; z) x4 \+ b) \m_pConnection->Open("Data Source=adotest;UID=saWD=;","","",adModeUnknown);
( K* O8 H" g2 L: ?( p  q/ w% ^5 Y5 ?. m" G0 c! w6 I) F
(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown); " ?) F9 ~8 n) O. q6 n) }

3 E0 v8 a$ e$ K# ]其中Server是SQL服务器的名称,DATABASE是库的名称
/ l# l  {5 M  O% E8 w
- K0 e+ s( d$ s! ?# ]  PConnection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State 5 o; D) f. B% b" x. @' \1 R1 E
ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒 ! L  l7 e1 M1 k" B$ K; _7 M
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);
/ f" E! z0 q8 Q$ c% w; ^
% O8 I0 D5 q8 g* _6 o
5 X; F( o! D; u) N$ Y1 X2 m+ IState属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如: # z5 H: S! |# Z8 y
if(m_pConnection->State)
3 R' C6 @* n0 V, P: o8 t' g     m_pConnection->Close(); ///如果已经打开了连接则关闭它 4 a0 t9 Z/ I" C# X* D. c+ G

# ^+ J' [) {: k3 C5 I+ k" b* Y5 o4 s. i* U! U- T; [
【4】执行SQL命令并取得结果记录集
( c: {- t. o% v* P* m5 a& n为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset; 4 o5 F& u! \2 X% K
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset"); , b# R7 T$ }0 f& o
SQL命令的执行可以采用多种形式,下面我们一进行阐述。 2 z3 L4 Z2 y. z6 L

9 d/ x# M0 O1 d$ u9 N: U+ ~(1)利用Connection对象的Execute方法执行SQL命令 % V" z1 R& }# z& f
Execute方法的原型如下所示: 1 k3 c0 \1 v9 @+ T( V, X, s
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一: 5 u& Z8 f  m0 c: I( m
adCmdText:表明CommandText是文本命令
& }. l; d9 g5 nadCmdTable:表明CommandText是一个表名
& b6 u2 b9 i; Q) i. i* {. s- padCmdProc:表明CommandText是一个存储过程 0 C- v$ ?1 l4 {7 l8 e
adCmdUnknown:未知 ! y& N% f2 q) @, ^0 K) v5 J# E

) N0 b2 p) U. m0 J6 wExecute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。   _variant_t RecordsAffected; $ U0 z4 M1 R7 ?7 T
  ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday # n& d/ J8 @& W; F
  m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",&RecordsAffected,adCmdText); + Q1 [2 ?9 H7 n5 V+ ~
  ///往表格里面添加记录
3 A+ k9 Y1 L  A8 i) J  m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) valueS (1, nullnullnullnullnullnullnullnullWashingtonnullnullnullnullnullnullnullnull,25,nullnullnullnullnullnullnullnull1970/1/1nullnullnullnullnullnullnullnull)",&RecordsAffected,adCmdText);
; A* [5 K# V2 W3 w" N5 K4 [" X  ///将所有记录old字段的值加一
9 a+ [' c- W" q+ _- R' A! W  W! E  m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); % G8 Y6 d0 K' S' a4 {
  ///执行SQL统计命令得到包含记录条数的记录集 0 {# `7 _( N3 i& y& R& X/ B
  m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); . e1 U7 z1 f( q/ S
  _variant_t vIndex = (long)0;
# D( X0 `/ w, i  _% I+ a3 k  _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
) i; G: `' I# q# J  m_pRecordset->Close();///关闭记录集
; J% _: h* I1 a& O' y- t  CString message; : }; w0 D+ j- @6 ?$ e/ c
  message.Format("共有%d条记录",vCount.lVal); 2 U* x, r6 d  n! @3 T# E% R( {
  AfxMessageBox(message);///显示当前记录条数
+ D- u, w8 s" V. x5 S8 Y4 f' v0 @
, b. q- J/ y& l7 e
- z) P2 I" w4 D$ W+ R(2)利用Command对象来执行SQL命令 / O" k' o0 l9 ]9 M: T- U
  _CommandPtr m_pCommand;
' @) e5 l8 R1 D  m_pCommand.CreateInstance("ADODB.Command"); ! W# S% J( Q/ g: V+ K) K1 d
  _variant_t vNULL; 7 Z2 d& G- A  @7 i3 k1 K. w/ u* t. Z
  vNULL.vt = VT_ERROR;
& l4 V, F* g) l2 N' d) k  vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
) A! F2 X' n% d! u* f$ A" G4 {+ E  m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它   t. W5 \, o4 y) W  d: }
  m_pCommand->CommandText = "SELECT * FROM users";///命令字串
& t; S+ S& ~6 y" J; `  E0 z  m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集
- R% Y" D) f% i& H; m4 E; I' [; l& d, ^# ?8 A+ d
在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。
  @2 j" {4 G! {
  s) ~/ O8 X) c/ L5 I' K+ g9 U
% S, Y" b$ Z9 u  U  r(3)直接用Recordset对象进行查询取得记录集 ! {9 A: |% n( Y  G: i+ S
例如
' }) E/ |% Y4 A$ ^, i( S2 J: J& W+ M! ]6 Z4 j8 ?
  m_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch *)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
1 U! z* Z4 }) s8 q4 r: w7 i, J" p4 M; m* a+ h$ j
Open方法的原型是这样的: # G- w$ E' x5 i! J. g5 ]3 f$ V
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )
' V7 n8 F+ V! O+ g2 g( `其中: , J$ s: A; F' `& `( A5 ?: `6 N( f, d& d
①Source是数据查询字符串
1 K; o! n1 @: r4 R5 v1 L! S/ J②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) . w: j6 K' d5 g7 v# t1 O
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
! |. f9 n6 p1 x3 z* Genum CursorTypeEnum
  N$ J* Z+ ]4 f2 s% `{
' V2 Q0 f: _7 b+ I# ZadOpenUnspecified = -1,///不作特别指定
1 O7 D# ~) c/ X- A: E0 w$ Y; badOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用 ! T4 }* X! Y' h! \
adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
4 p( v) Q& _# Q  s. dadOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。 4 x8 y  `  \2 `
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。 , L  O8 N! z; @! z9 T
};
( ]* }1 M5 y" _% ^+ O3 t7 k6 w④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
5 }4 F4 @, f! ?1 Nenum LockTypeEnum
" r2 b- o4 a: ]. a* c{ ) N3 F  s8 `. X2 H
adLockUnspecified = -1,///未指定
9 U% |1 l* @* Q2 XadLockReadOnly = 1,///只读记录集 2 N, I9 R% t& u$ i; I! b
adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制 ; `9 j2 p6 E# c. X
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作 6 }- Q/ x$ M, [: ^/ q
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。 2 L1 I" R+ x. E. A% X
};
2 |& I0 v! B" g" {4 F5 p⑤Options请参考本文中对Connection对象的Execute方法的介绍
% M5 _# \7 |$ `" E( m5 V3 n6 g. K: p7 Q- k# w
* R6 A; k: G/ v: m, r, n+ S
【5】记录集的遍历、更新
' z+ w8 \3 w; a* |' m根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday 9 Y" ]  M+ H; c% c4 Y+ ?
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。
8 z1 z0 z: V# h2 w- |, \/ E/ j6 t; L9 e* b! {8 d5 X0 n! N7 ?( r
, a; G9 i* T* j# E& c) I
_variant_t vUsername,vBirthday,vID,vOld; ' m6 q+ d3 n2 B5 n/ j& g* F
_RecordsetPtr m_pRecordset;
% O* g7 l* a/ v. u. L6 _- w% Jm_pRecordset.CreateInstance("ADODB.Recordset");
3 a) E9 o& T& ~9 Rm_pRecordset->Open("SELECT * FROM users",_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText); 6 @1 S: W- K$ a! D6 c
while(!m_pRecordset->adoEOF)///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","adoEOF")这一句吗?
. M; @7 S/ `9 T{ # Y2 z, v- \- h
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,你也可以直接给出列的名称,如下一行 ! e/ Z: y6 S1 U: R- P; e, s
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
7 W1 `6 ]0 o4 @$ S3 jvOld = m_pRecordset->GetCollect("old"); ( F( a. T1 D# N9 e0 b$ S8 \
vBirthday = m_pRecordset->GetCollect("birthday");
, J1 `7 m2 {+ M9 B///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
( R( o) }$ F* c) Pif(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
; |' Z0 G, O, @  TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday); . e+ W( l' q( a: q# C: a5 D! K+ G
m_pRecordset->MoveNext();///移到下一条记录 + y- S- W3 e) [- c& y2 [
} + g& [1 ?! B/ V( h% D+ F
m_pRecordset->MoveFirst();///移到首条记录
4 v& m' m( H" Fm_pRecordset->Delete(adAffectCurrent);///删除当前记录
! b% a4 V7 D1 b, F( _0 N///添加三条新记录并赋值 / Z! B' N+ Y# e  I
for(int i=0;i<3;i++) 0 R- R$ J* v( n* I& _% `! P4 L
{
; e, P8 S3 j& m' q9 F* Y2 Vm_pRecordset->AddNew();///添加新记录
/ O5 i8 V6 o4 P. pm_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
8 {6 _7 S4 n! z$ p! @' cm_pRecordset->PutCollect("username",_variant_t("叶利钦"));
/ a, X: l) f7 Tm_pRecordset->PutCollect("old",_variant_t((long)71));
$ f" w) z; U- c+ nm_pRecordset->PutCollect("birthday",_variant_t("1930-3-15")); - a0 Q% ?) K* z0 ^
} : J0 e: z9 X- r6 d+ `5 o
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
7 z& G# B7 Z8 Pm_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄 9 d. O; c3 b/ D+ q+ r
m_pRecordset->Update();///保存到库中 2 r% ?" C0 e7 i6 z+ Q- l
5 r2 _" A- d) M% M( R, Z
【6】关闭记录集与连接
# |2 u) z2 R& \1 b7 i记录集或连接都可以用Close方法来关闭
$ z( Z6 m7 _0 H2 }7 B+ |      m_pRecordset->Close();///关闭记录集
" }9 G3 W1 l5 R/ b3 J4 L      m_pConnection->Close();///关闭连接
! Q+ l7 o  v4 r) r$ p* ~) t  e# O) L0 {- ^/ q, L+ L/ J
至此,我想您已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子中读出所有记录放到列表控件中、并可以添加、删除、修改记录。
8 \! O  \8 @4 W+ {5 i9 c1 O  |点这里下载示例代码 3 ~' c9 G8 ]3 o3 B! j; B
/ @7 j6 G* H! `8 L
后记:限于篇幅ADO中的许多内容还没有介绍,下次我们将详细介绍Recordset对象的属性、方法并解决几个关键的技术:绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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