VC使用数据库对所需的数据进行存储与管理。本文对ODBC、DAO、OLE/DB和ADO这几种技术进行该有的说明: 1、ODBC ODBC(Open Database Connection开发数据库互连)是一种使用SQL的数据库应用程序设计接口。它建立了一组规范,并提供了一组对数据库访问的标准API(
VC使用数据库对所需的数据进行存储与管理。本文对ODBC、DAO、OLE/DB和ADO这几种技术进行该有的说明:
件"sql.h","sqlext.h","sqhypes.h"。MFC对ODBC的封装主要是开发了CDataBase类和CRecordSet类。CDataBase类:该类可以实现对数据源的操作。通过构造CDataBase类对象,调用它的Open或者OpenEx成员函数建立数据源连接。CRecordSet类:记录集类提供从数据源中提取的结果记录集并对其进行操作。它通过SQL语句返回,可以对表和试图进行查询或存储。要注意的是在应用程序中,一般不直接使用CRecordSet类而是使用其派生类,因为当使用MFC Class Wizard产生一个CRecordSet类的派生类时MFC Class Wizard会自动添加相应数据库表中字段的成员变量并自动重载CRecordSet类的成员函数DoFieldExchange()。通过使用RFX函数来完成数据库字段与记录集字段数据成员变量的数据交换。在程序中使用MFC ODBC,除登陆数据源之外,还需要在stdafx.h中包含afxdb.h。
用DAO方法不需要登录数据源,只要指明数据库的路径即可。VC++的ODBC和DAO方法在使用形式上比较相似,ODBC访问记录集的基类是CRecordSet,DAO则为CDaoRecordSet,差别就是"DAO",通过定义这些类的对象并调用相应的成员函数就可以实现Access数据库和表的动态创建。ODBC存取数据库时使用RFX_LongBinary。使用AppWizard的过程基本上与使用ODBC时相似,只是在选择数据源时要选择已建立好的DAO数据源,在后从系统中选出源数据库和表,令其自动生成这部分的代码。在应用程序中使用MFC DAO可以通过以下步骤来实现:
首先应确保在stdafx.h头文件中包含了afxado.h头文件: #include "afxdao.h" 接着创建自己的CDaoRecordSet类(设为MyDaoRecordSet),并使它与你所需要的数据库的表相连。同时在文档类的 OnOpenDocument()函数中加人: m_pdb = new CDaoDataBase(); m_pdb->open(); m_pset = new MyDaoRecordSet(m_pdb); m_pset->open();
最后创建自己的对话框,选择MFC Class Wizard的ClassInfo页。在ForeignClass项填入先前所创建的MyDaoRecordSet,在ForeignVariable项填入m_pset,并对各个edit栏用变量映射。
DAO也存在较大的缺点:主要是MFC DAO与Jet数据库引擎之间的版本。由于VC++.NET环境和向导不再支持DAO(虽然扔包含DAO类),用户必须手工编写过去由向导执行的任务,Microsoft建议新项目采用OLE/DB模板或ODBC,OLE/DB是基于COM接口来访问数据库,所以它继承了COM接口所有特性,具有较好的稳定性,由于接口的标志性,使得程序可以被应用到任何提供了数据程序的数据源,因此使用OLE/DB开发的程序具有比较好的移植性。另外OLE/DB通过将数据库的功能分为客户和服务器两个方面,提高了数据库访问效率。前面提到的传统数据库访问技术(ODBC、DAO)都只能访问关系型数据库,而OLE/DB可以访问任何形式的文件系统,包括传统的关系型数据库和非关系型数据源及用户自定义的文件格式。但是在基于COM技术的数据库访问中通常使用ADO技术而不是OLE/DB。
ADO对象模型包括了7个对象:1)数据连接(Connection)对象建立一个数据交换环境,应用程序通过Connection访问数据;2)命令(Command)对象通过已建立的Connection发布命令完成操作数据库功能;3)记录集(RecordSet)对象存储查询命令返回的结果;4)错对象(Error)是记录应用ADO过程中出现错误的信息;5)属性(Property)对象代表数据提供者的具体属性;6)字段(Field)对象代表一个记录集的一个域;7)参数(Parameter)对象代表SQL存储过程或有参数查询中的参数。其中Connection、Command和RecordSet三个对象时主体对象,它们可以被独立创建和释放。Connection对象是用来建立和维护与数据源的连接;Command对象对数据创建和执行对数据库的操作命令,可以使用该对象执行SQL语句或调用存储过程;Command对象执行后将返回一个RecordSet对象。RecordSet对象包含执行数据库操作后返回一个记录集。通过该对象可以浏览和更新数据库中保存的记录。
::Coinitialize(NULL);1)在MFC中没有可与ADO一起使用的类,因此若要在VC++程序中使用ADO对象,首先需要导入ADO动态链接库,在创建的VC工程的stadafx.h头文件中添加如下代码
#import "C:\Program Files\Common Files\System\ado\msado15.dll\no_namespace" Rename("EOF","EndOfFile");其中no_namespace属性表示编辑器不继承本身的命名空间,rename("EOF","EndOfFile")表示将所有"EOF"替换为"EndOfFile"。为了方便使用ADO的各个类,应该在程序开头调用#import指令,在ADO的类库中包含了3个智能指针ConnectionPtr、CommandPtr和RecordSetPtr,其中ConnectionPtr指针用来管理连接一个数据源提供者所要求的信息,它提供了很多方法如Open和Close可以打开和关闭一个到数据库提供者的活动连接(Live Connection),还可以通过Excute方法在数据源上执行命令,而RecordSetPtr封装了数据源提供者返回的记录,它可以用于浏览返回的结果,也可以用于插入、删除或修改已有记录。为了代码的安全性,必须在函数调用的地方添加"try...catch..."程序块:
try{ m_pRs = m_pConn->Excute(...) return m_pRs; }cath(_corn_error& a_pComError){ TraceCOMExceptions(a_pComError)); return NULL; }2)使用Connection对象和数据库建立连接
::Couninitialize(NULL);
重点说明一下ADO的智能指针:
1、_ConnectionPtr智能指针,通常用于打开、关闭一个库连接或用它的Execute方法来执行一个不返回结果的命令语句(用法和_CommandPtr中的Execute方法类似)。打开一个库连接。先创建一个实例指针,再用Open打开一个库连接,它将返回一个IUnknown的自动化接口指针。
_ConnectionPtr m_pConnection; // 初始化COM,创建ADO连接等操作 AfxOleInit(); m_pConnection.CreateInstance(__uuidof(Connection)); // 在ADO操作中建议语句中要常用try...catch()来捕获错误信息, // 因为它有时会经常出现一些意想不到的错误。jingzhou xu try { // 打开本地Access库Demo.mdb m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Demo.mdb","","",adModeUnknown); } catch(_com_error e) { AfxMessageBox("数据库连接失败,确认数据库Demo.mdb是否在当前路径下!"); return FALSE; }关闭一个库连接。如果连接状态有效,则用Close方法关闭它并赋于它空值。代码如下所示
if(m_pConnection->State) m_pConnection->Close(); m_pConnection= NULL;
2、_RecordsetPtr智能指针,可以用来打开库内数据表,并可以对表内的记录、字段等进行各种操作。――打开数据表。打开库内表名为DemoTable的数据表,代码如下:
_RecordsetPtr m_pRecordset; m_pRecordset.CreateInstance(__uuidof(Recordset)); // 在ADO操作中建议语句中要常用try...catch()来捕获错误信息, // 因为它有时会经常出现一些意想不到的错误。jingzhou xu try { m_pRecordset->Open("SELECT * FROM DemoTable", // 查询DemoTable表中所有字段 theApp.m_pConnection.GetInterfacePtr(), // 获取库接库的IDispatch指针 adOpenDynamic, adLockOptimistic, adCmdText); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }读取表内数据。将表内数据全部读出并显示在列表框内,m_AccessList为列表框的成员变量名。如果没有遇到表结束标志adoEOF,则用GetCollect(字段名)或m_pRecordset->Fields->GetItem(字段名)->Value方法,来获取当前记录指针所指的字段值,然后再用MoveNext()方法移动到下一条记录位置。代码如下所示:
_variant_t var; CString strName,strAge; try { if(!m_pRecordset->BOF) m_pRecordset->MoveFirst(); else { AfxMessageBox("表内数据为空"); return; } // 读入库中各字段并加入列表框中 while(!m_pRecordset->adoEOF) { var = m_pRecordset->GetCollect("Name"); if(var.vt != VT_NULL) strName = (LPCSTR)_bstr_t(var); var = m_pRecordset->GetCollect("Age"); if(var.vt != VT_NULL) strAge = (LPCSTR)_bstr_t(var); m_AccessList.AddString( strName + " --> "+strAge ); m_pRecordset->MoveNext(); } // 默认列表指向第一项,同时移动记录指针并显示 m_AccessList.SetCurSel(0); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }插入记录。可以先用AddNew()方法新增一个空记录,再用PutCollect(字段名,值)输入每个字段的值,最后再Update()更新到库中数据既可。其中变量m_Name和m_Age分别为姓名及年龄编辑框的成员变量名。代码所下所示:
try { // 写入各字段值 m_pRecordset->AddNew(); m_pRecordset->PutCollect("Name", _variant_t(m_Name)); m_pRecordset->PutCollect("Age", atol(m_Age)); m_pRecordset->Update(); AfxMessageBox("插入成功!"); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }移动记录指针。移动记录指针可以通过MoveFirst()方法移动到第一条记录、MoveLast()方法移动到最后一条记录、MovePrevious()方法移动到当前记录的前一条记录、MoveNext()方法移动到当前记录的下一条记录。但我们有时经常需要随意移动记录指针到任意记录位置时,可以使用Move(记录号)方法来实现,注意: Move()方法是相对于当前记录来移动指针位置的,正值向后移动、负值向前移动,如:Move(3),当前记录是3时,它将从记录3开始往后再移动3条记录位置。代码如下所示:
try { int curSel = m_AccessList.GetCurSel(); // 先将指针移向第一条记录,然后就可以相对第一条记录来随意移动记录指针 m_pRecordset->MoveFirst(); m_pRecordset->Move(long(curSel)); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }修改记录中字段值。可以将记录指针移动到要修改记录的位置处,直接用PutCollect(字段名,值)将新值写入并Update()更新数据库既可。可以用上面方法移动记录指针,修改字段值代码如下所示:
try { // 假设对第二条记录进行修改 m_pRecordset->MoveFirst(); m_pRecordset->Move(1); // 从0开始 m_pRecordset->PutCollect("Name", _variant_t(m_Name)); m_pRecordset->PutCollect("Age", atol(m_Age)); m_pRecordset->Update(); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }删除记录。删除记录和上面修改记录的操作类似,先将记录指针移动到要修改记录的位置,直接用Delete()方法删除它并用Update()来更新数据库既可。代码如下所示:
try { // 假设删除第二条记录 m_pRecordset->MoveFirst(); m_pRecordset->Move(1); // 从0开始 m_pRecordset->Delete(adAffectCurrent); // 参数adAffectCurrent为删除当前记录 m_pRecordset->Update(); } catch(_com_error *e) { AfxMessageBox(e->ErrorMessage()); }关闭记录集。直接用Close方法关闭记录集并赋于其空值。代码如下所示:
m_pRecordset->Close(); m_pRecordset = NULL;
3、CommandPtr智能指针,可以使用_ConnectionPtr或_RecordsetPtr来执行任务,定义输出参数,执行存储过程或SQL语句。 执行SQL语句。先创建一个_CommandPtr实例指针,再将库连接和SQL语句做为参数,执行Execute()方法既可。代码如下所示:
_CommandPtr m_pCommand; m_pCommand.CreateInstance(__uuidof(Command)); m_pCommand->ActiveConnection = m_pConnection; // 将库连接赋于它 m_pCommand->CommandText = "SELECT * FROM DemoTable"; // SQL语句 m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText); // 执行SQL语句,返回记录集执行存储过程。执行存储过程的操作和上面执行SQL语句类似,不同点仅是CommandText参数中不再是SQL语句,而是存储过程的名字,如Demo。另一个不同点就是在Execute()中参数由adCmdText(执行SQL语句),改为adCmdStoredProc来执行存储过程。如果存储过程中存在输入、输出参数的话,需要使用到另一个智能指针_ParameterPtr来逐次设置要输入、输出的参数信息,并将其赋于_CommandPtr中Parameters参数来传递信息,有兴趣的读者可以自行查找相关书籍或MSDN。执行存储过程的代码如下所示:
_CommandPtr m_pCommand; m_pCommand.CreateInstance(__uuidof(Command)); m_pCommand->ActiveConnection = m_pConnection; // 将库连接赋于它 m_pCommand->CommandText = "Demo"; m_pCommand->Execute(NULL,NULL, adCmdStoredProc);
连接并打开数据库方式:
1)通过JET数据库引擎对Access数据库的连接:
对于2003的:m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Demo.mdb","","",adModeUnknown); 对于2007的:m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.12.0;Data Source=Demo.mdb","","",adModeUnknown);2)通过DSN数据源对任何支持ODBC的数据库进行连接:
m_pConnection->Open("Data Source=adotest;UID=sa;PWD=;","","",adModeUnknown);
3)不通过DSN对SQL SERVER数据库进行连接:
m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown);
其中Server是SQL服务器的名称,DATABASE是库的名称
----- ADO连接SQL Server的数据库连接字符串模板---------- 身份验证模式为:"sql server和windows" Provider=SQLOLEDB.1;Persist Security Info=True;User ID=用户名;Password=密码;Initial Catalog=数据库名;Data Source=SQL服务器名 身份验证模式为:"仅windows" Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=数据库名;Data Source=SQL服务器名说明:以上两种方式效果一样,不一样的地方是安全性问题,本人建议采用第一种(身份验证模式为:"sql server和windows")
以下是网上对两种身份验证模式的说明:
主要集中在信任连接和非信任连接。
windows 身份验证相对于混合模式更加安全,使用本连接模式时候,sql不判断sa密码,而仅根据用户的windows权限来进行身份验证,我们称为“信任连接”,但是在远程连接的时候会因NTML验证的缘故,无法登陆。
混合模式验证就比较既当本地用户访问sql时候采用windows身份验证建立信任连接,当远程用户访问时由于未通过windows认证,而进行sql server认证(使用sa的用户也可以登录sql),建立“非信任连接”,从而使得远程用户也可以登录。
更加直接一些就是windows身份验证,不验证sa密码,如果windows登录密码不正确,无法访问sql,混合模式既可以使用windows身份验证登录,有可以在远程使用sa密码登录。
准确来说,混合身份验证模式,也就是基于Windows身份验证和SQL Server身份混合验证。在这个模式中,系统会判断账号在Windows操作系统下是否可信,对于可信连接,系统直接采用Windows身份验证机制,而非可信连接,这个连接不仅包括远程用户还包括本地用户,SQL Server 会自动通过账户的存在性和密码的匹配性来进行验证。比如当SQL Server实例在Windows 98上运行时,必须使用混合模式,因为在Windows 98上不支持Windows身份验证模式。