1、ODBC( Open Database Connectivity,开放数据库连接)是由 Microsoft 定义的一种数据库访问标准,它提供了一种标准的数据库访问方法以访问不同数据库提供商的数据库,其本质上是一组数据库访问API。虽然数据库访问有多种方法,但 ODBC 以其编程相对简单,在实际 编程中被广泛使用。VC+中提供了一组封装了 ODBC API 的 MFC ODBC 类,以减少程序代码编写量。在 VC+中使用MFC ODBC 类访问数据库时,一般都是先配置或选择已有的数据源,再构造 CDatabase 类对象,打开数据源,用该数据库类对象和 SQL(结构化查询语言)可以对库进行访问,
2、再构造 CRecordset 类或其继承类对象,用数据集类对象和 SQL 可以实现对库中的表的操作。在 VC+中用 MFC ODBC 类操作 SQL Server 数据库基本上也是这个思路。1.配置数据源在程序中根据用户选择动态配置数据源而不调用 ODBC 数据源管理器,对于应用程序开发有时是十分必要的。毕竟 ODBC 数据源管理器对于对数据库不熟悉的用户显的复杂了,且 ODBC 数据源管理器的界面风格有可能与整个应用程序的界面风格不一致,对于严谨的应用程序开发者来说,这些都是不能容忍的。配置 SQL Server 数据源时,必须有 SQL Server 服务器名和服务器中的目标数据库名(否则
3、打开该数据源时,就会弹出配置数据源对话框或失败)。这与配置 Access 等数据库的数据源时指定数据库文件的路径不同。可以通过 ODBC API 函数 SQLBrowseConnect 得到本地所有的 SQL Server 服务器、服务器中的库、语言信息等。其使用方法如下所示:得到服务器名SQLBrowseConnect(hdbc, “DRIVER=SQL Server;“, SQL_NTS, BrowseResult, sizeof(BrowseResult), 其中 hdbc 是用 SQLAllocHandle 函数得到的连结句柄,第二个参数是指定连结属性的输入字符串,第三个参数是输入字符
4、串的长度,第四个参数是输出字符串的指针,我们要得到的信息就存于这个字符串中,第五个参数指定输出字符串的长度,第六个参数是实际返回字符串的长度。BrowseResult 所指向的函数返回的字符串中含有形如“SERVER:Server=Server_name1,Server_name2,” 的字符串,其中Server_name1、Server_name2 就是 SQL Server 服务器名。得到库名当用户选定一个服务器之后,可以进一步利用 SQLBrowseConnect 函数得到服务器中存在的库或语言信息(如果需要的话)。SQLBrowseConnect(hdbc,“SERVER=Server
5、_name1;UID=sa;PWD=515578;“,SQL_NTS, BrowseResult, sizeof(BrowseResult), 其中 UID 和 PWD 是访问该服务器的用户名和密码。这次 BrowseResult 所指向的函数返回的字符串中含有形如 DATABASE:Database=master,model,和 LANGUAGE:Language=Arabic, Brazilian, English, 的字符串,其中 master、model 为服务器中的数据库名。利用这些得到信息就可以配置 SQL Server 数据源了:SQLConfigDataSource(NULL,
6、ODBC_ADD_DSN,“SQL Server“,“DSN=myDSN0 SERVER=xhm0DATABASE=pubs00“ )作者有幸在网上找到 Santosh Rao 编写的 CSQLInfoEnumerator 类,该类有 EnumerateSQLServers、EnumerateDatabase、EnumerateDatabaseLanguage 三个成员函数,其封装了SQLBrowseConnect 函数并进行相应的字符处理,大大简化了 SQLBrowseConnect 函数的使用。2.与数据源建立连接如果使用 ODBC API 函数进行连结,函数 SQLConnect、SQL
7、DriverConnect 和 SQLBrowseConnect 都可以实现。其中 SQLConnect 函数用于给定所有参数直接建立与数据源的连接;SQLDriverConnect 用于给定了部分连接参数,并弹出数据源浏览窗口与用户交互,获得足够的参数后建立与数据源的连接;而SQLBrowseConnect 是通过迭代获取连结参数再进行连接,其使用如前所述。MFC 为连接数据源提供了一个数据库类 CDatabase,通过它我们可以非常方便地与数据源建立连结:/m_db 是 CDatabase 的对象/m_szUserId,m_szPassword 是 CString 对象,为访问数据源的用户
8、名和密码CString str;str = _T(“DSN=xhmtest;UID=“)+m_szUserId+_T(“;PWD=“)+m_szPassword;if (! m_db.OpenEx(str, CDatabase:noOdbcDialog ) )AfxMessageBox(“打开数据源失败“);另外,如果我们想对数据源进行操作,就可以利用打开的 CDatabase 对象执行 SQL 语句实现。如新建一张表:str = “CREATE TABLE TableDemo (Column1TEXT, Column2 NUMBER)“;m_db.ExecuteSQL(sSql);3.得到数
9、据库的结构信息在 VC+中可以单独利用 ODBC API 获取数据库的结构信息,但方法非常复杂,各种参数也不易理解,且返回的结果不易获取。MFC 中也没有为应用程序开发者得到数据库结构信息而提供单独的类。但是我们还是可以通过一些已有的方法方便地获得数据库的结构信息。3.1 得到数据库中的表利用 ODBC API 函数 SQLTables 可以得到数据库中的表信息,但使用不易。在 MSDN 的 sample 中有一个 catalog 例程,它示范了如果得到一个数据库的表信息和表中的字段信息,该程序中有两个非常实用的类:Ctables 和 Ccolumns。其中 CTables 继承 CRecor
10、dset 类,通过非常巧妙的方法封装了 SQLTables 函数,并将结果集存于 CRecordset 类,方便获取。在程序中我们可以这样应用:/m_db 是已经打开的 CDatabse 对象 ,/m_strTableOwner 是 CComboBox 对象CTables rs(rs.Open(NULL, NULL, NULL, “TABLE“);CString strTableRef;m_combTable.ResetContent();while (!rs.IsEOF()strTableRef = _T(“);if (!rs.m_strTableOwner.IsEmpty()strTabl
11、eRef += rs.m_strTableOwner + _T(“.“);strTableRef += rs.m_strTableName + _T(“);m_combTable.AddString(strTableRef);rs.MoveNext();rs.Close();在 CTables 对象 Open 方法第四个能数指定“TABLE” 是指返回库中的表,当该参数为“SYSTEM TABLE”时返回系统表,当为 NULL 时,返回所有表。3.2 得到表中字段结构得到表中字段结构有三种具体实现方式:直接利用 ODBC API 函数 SQLColumn;其实现也同样复杂。利用前面提到的封装了
12、 SQLColumns 函数的 CColumns 类;其使用方法与 CTables 类似,其成员变量m_strColumnName 即为字段名利用 CRecordset 类的成员函数。 MFC 在 CRecordset 类中提供了获取表结构信息方法,使用非常方便。(但很奇怪为什么 CDatabase 类没有获取库结构信息的类方法?)我们以利用 CRecodrset 为例简单说明怎样得到表结构。假设现在要得到用户在 CComboBox 对象中所选表的所有字段名,并将它埴入一个 CListCtrl 对象(其 View 属性被设为 Report)的列名中去:/m_strTableOwner 和上同,
13、为 CComboBox 对象/m_listData 为 ClistCtrl 对象CRecordset rsCODBCFieldInfo info;CString strSQL;m_combTable.GetLBText(m_combTable.GetCurSel(), strSQL);strSQL = _T(“SELECT * FROM “) + strSQL;rs.Open(Open(CRecordset:snapshot, strSQL, CRecordset:readOnly);int nColumns = rs.GetODBCFieldCount();for (int nNum = 0
14、; nNum GetODBCFieldInfo(nNum, info);m_listData.InsertColumn(nNum, info.m_strName, LVCFMT_LEFT, 80)另外,我们可以利用 CRecordSet:GetFieldValue 函数通过指定列名或列序数得到记录集中游标所指记录的值,通过CRecordset:AddNew、CRecordset:CancelUpdate、CRecordset:Delete、CRecordset:Edit 、CRecordset:Update 等函数操作 CRecordset 打开的表。(1).在文件 stdafx.h 中最后一
15、个#endif 的前一行写入 #import “C:program filescommon filesSystemadomsado15.dll“ no_namespace rename(“EOF“,“EndOfFile“) rename(“LockTypeEnum“,“newLockTypeEnum“) rename(“DataTypeEnum“,“newDataTypeEnum“) rename(“FieldAttributeEnum“,“newFieldAttributeEnum“) rename(“EditModeEnum“,“newEditModeEnum“) rename(“Reco
16、rdStatusEnum“,“newRecordStatusEnum“) rename(“ParameterDirectionEnum“,“newParameterDirectionEnum“) 如果你的系统不是安装在 C 盘的话就把#import 后面的 C 改成系统所有的盘 (2).在 C*App 类的 public:下加入 _RecordsetPtr m_pADOSet; bool ADOExecute(_RecordsetPtr 在 private:下加入_ConnectionPtr ADOConn; 在 class C*App : public CWinApp . ;之后#endif
17、 之前加入 extern C*App theApp; (3)在 BOOL C*App:InitInstance()函数中 Enable3dControls(); / Call this when linking to MFC statically 这一行下面加入 if( FAILED(:CoInitialize(NULL) ) AfxMessageBox(“ADO Init failed“); return false; try ADOConn.CreateInstance(_uuidof(Connection); ADOConn-Open(“DSN=OBDC 数据源;Provider=MSD
18、ASQL“,“用户“,“密码“, adConnectUnspecified);/这一行要自已修改 catch(_com_error err.Format(“%s“, (char*)(e.Description() ); AfxMessageBox(err); catch(.) AfxMessageBox(“Unknown Error.“); m_pADOSet.CreateInstance(_uuidof(Recordset); 并在文件最后加上如下代码: bool C*App:ADOExecute(_RecordsetPtr try ADOSet-Open(strSQL, ADOConn.G
19、etInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdUnknown); return true; catch(_com_error err.Format(“ADO Error: %s“,(char*)e.Description(); AfxMessageBox(err); return false; 最后就可以在登录时执行 SQL 语句了,比如用户为 CString strUser, 密码是 CString strPwd;数据库表是 user_table(user_id, user_name, user_pwd)则 _variant_t
20、 strQuery, Holder; strQuery = “select * from user_table where user_name=“+strUser +“ and user_pwd=“+ strPwd +“; theApp.ADOExecute(theApp.m_pADOSet, strQuery); int iCount = theApp.m_pADOSet-GetRecordCount(); if ( 0=iCount ) AfxMessageBox(_T(“密码错误“), MB_ICONEXCLAMATION); return; else AfxMessageBox(_T(
21、“登录成功“), MB_ICONEXCLAMATION); VC 用 ADO 访问数据库全攻略,介绍了 VC 用 ADO 来访问数据库的各个对象及各方法,很经典,也很实用,很值得一看。 正文 一、ADO 概述 ADO 是 Microsoft 为最新和最强大的数据访问范例 OLE DB 而设计的,是一个便于使用的应用程序层接口。ADO 使您能够编写应用程序以通过 OLE. DB 提供者访问和操作数据库服务器中的数据。ADO 最主要的优点是易于使用、速度快、内存支出少和磁盘遗迹小。ADO 在关键的应用方案中使用最少的网络流量,并且在前端和数据源之间使用最少的层数,所有这些都是为了提供轻量、高性能的
22、接口。之所以称为 ADO,是用了一个比较熟悉的暗喻,OLE 自动化接口。 OLE DB 是一组” 组件对象模型”(COM) 接口,是新的数据库低层接口,它封装了 ODBC 的功能,并以统一的方式访问存储在不同信息源中的数据。OLE DB 是 Microsoft UDA(Universal Data Access)策略的技术基础。OLE DB 为任何数据源峁烁咝阅艿姆梦剩庑 菰窗 叵岛头枪叵凳 菘狻缱佑始 臀募 低场谋竞屯夹巍远逡滴穸韵蟮鹊取 R 簿褪撬担琌 LE DB 并不局限于 ISAM、Jet 甚至关系数据源,它能够处理任何类型的数据,而不考虑它们的格式和存储方法。在实际应用中,这种多样性
23、意味着可以访问驻留在 Excel 电子数据表、文本文件、电子邮件 /目录服务甚至邮件服务器,诸如 Microsoft Exchange 中的数据。但是,OLE DB 应用程序编程接口的目的是为各种应用程序提供最佳的功能,它并不符合简单化的要求。您需要的 API 应该是一座连接应用程序和 OLE DB 的桥梁,这就是 ActiveX Data Objects (ADO)。 二、在 VC 中使用 ADO(开发步骤好下:) 1、引入 ADO 库文件 使用 ADO 前必须在工程的 stdafx.h 头文件里用直接引入符号 #import 引入 ADO 库文件,以使编译器能正确编译。代码如下所示: 用#
24、import 引入 ADO 库文件 #import “c:program filescommon filessystemadomsado15.dll“no_namespaces rename(“EOF“ adoEOF“) 这行语句声明在工程中使用 ADO,但不使用 ADO 的名字空间,并且为了避免常数冲突,将常数 EOF 改名为 adoEOF。现在不需添加另外的头文件,就可以使用 ADO 接口了。 2、初始化 OLE/COM 库环境 必须注意的是,ADO 库是一组 COM 动态库,这意味应用程序在调用 ADO 前,必须初始化 OLE/COM 库环境。在 MFC 应用程序里,一个比较好的方法是在
25、应用程序主类的InitInstance 成员函数里初始化 OLE/COM 库环境。 BOOL CMyAdoTestApp:InitInstance() if(!AfxOleInit()/这就是初始化 COM 库 AfxMessageBox(“OLE 初始化出错!”); return FALSE; 3、ADO 接口简介 ADO 库包含三个基本接口:_ConnectionPtr 接口、_CommandPtr 接口和_RecordsetPtr 接口。 _ConnectionPtr 接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的 SQL 语句,如一个存储过程。使
26、用_ConnectionPtr 接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr 来实现。而用 _ConnectionPtr 操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr 时不需要。 _CommandPtr 接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和 SQL 语句。在使用_CommandPtr 接口时,你可以利用全局_ConnectionPtr 接口,也可以在_CommandPtr 接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记
27、录集,那么,你应该使用全局_ConnectionPtr 接口创建一个数据连接,然后使用 _CommandPtr 接口执行存储过程和SQL 语句。 _RecordsetPtr 是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同 _CommandPtr 接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给_RecordsetPtr 的 connection 成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同 Command 对象一样使用已经创建了数据连接的全局 _ConnectionPtr 接口 ,然后使
28、用_RecordsetPtr 执行存储过程和 SQL 语句。 4、使用_ConnectionPtr 接口 _ConnectionPtr 主要是一个连接接口,取得与数据库的连接。它的连接字符串可以是自己直接写,也可以指向一个 ODBC DSN。 。 _ConnectionPtr pConn; if (FAILED(pConn.CreateInstance(“ADODB.Connection“) AfxMessageBox(“Create Instance failed!“); return; CString strSRC; strSRC=“Driver=SQL Server;Server=“;
29、strSRC+=“suppersoft“; strSRC+=“;Database=“; strSRC+=“mydb“; strSRC+=“;UID=SA;PWD=“; CString strSQL = “Insert into student(no,name,sex,address) values(3,“aaa“,“male“,“beijing“)“; _variant_t varSRC(strSRC); _variant_t varSQL(strSQL); _bstr_t bstrSRC(strSRC); if (FAILED(pConn-Open(bstrSRC,“,“,-1) AfxMe
30、ssageBox(“Can not open Database!“); pConn.Release(); return; COleVariant vtOptional(long)DISP_E_PARAMNOTFOUND,VT_ERROR); pConn-Execute(_bstr_t(strSQL), pConn.Release(); AfxMessageBox(“ok!“); 5、使用_RecordsetPtr 接口(以连接 SQL Server 为例) _RecordsetPtr pPtr; if (FAILED(pPtr.CreateInstance(“ADODB.Recordset“)
31、 AfxMessageBox(“Create Instance failed!“); return FALSE; CString strSRC; strSRC=“Driver=SQL Server;Server=“; strSRC+=“210.46.141.145“; strSRC+=“;Database=“; strSRC+=“mydb“; strSRC+=“;UID=sa;PWD=“; strSRC+=“sa“; CString strSQL = “select id,name,gender,address from personal“; _variant_t varSRC(strSRC)
32、; _variant_t varSQL(strSQL); if(FAILED(pPtr-Open(varSQL,varSRC,adOpenStatic,adLockOptimistic,adCmdText) AfxMessageBox(“Open table failed!“); pPtr.Release(); return FALSE; while(!pPtr-GetadoEOF() _variant_t varNo; _variant_t varName; _variant_t varSex; _variant_t varAddress; varNo = pPtr-GetCollect (
33、“id“); varName = pPtr-GetCollect (“name“); varSex = pPtr-GetCollect (“gender“); varAddress = pPtr-GetCollect (“address“); CString strNo =(char *)_bstr_t(varNo); CString strName =(char *)_bstr_t(varName); CString strSex =(char *)_bstr_t(varSex); CString strAddress =(char *)_bstr_t(varAddress); strNo.
34、TrimRight(); strName.TrimRight(); strSex.TrimRight(); strAddress.TrimRight(); int nCount = m_list.GetItemCount(); int nItem = m_list.InsertItem (nCount,_T(“); m_list.SetItemText (nItem,0,strNo); m_list.SetItemText (nItem,1,strName); m_list.SetItemText (nItem,2,strSex); m_list.SetItemText (nItem,3,st
35、rAddress); pPtr-MoveNext(); pPtr-Close(); pPtr.Release(); 6、使用_CommandPtr 接口 _CommandPtr 接口返回一个 Recordset 对象,并且提供了更多的记录集控制功能,以下代码示例了使用_CommandPtr 接口的方法: 代码 11:使用_CommandPtr 接口获取数据 _CommandPtr pCommand; _RecordsetPtr pRs; pCommand.CreateInstance(_uuidof(Command); pCommand-ActiveConnection=pConn; pCom
36、mand-CommandText=“select * from student“; pCommand-CommandType=adCmdText; pCommand-Parameters-Refresh(); pRs=pCommand-Execute(NULL,NULL,adCmdUnknown); _variant_t varValue = pRs-GetCollect(“name“); CString strValue=(char*)_bstr_t(varValue); 6、关于数据类型转换由于 COM 对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据, 因此 Cstring 类和 COM 对象是不兼容的,我们需要一组 API 来转换 COM 对象和 C+类型的数据。_vatiant_t 和_bstr_t 就是这样两种对象。它们提供了通用的方法转换 COM 对象和C+类型的数据。学习 ado 和 odbc 访问数据库的一些思路整理- VC 编程http:/