Visual C++数据库开发技术
Visual C++为数据库开发提供了多种多样的技术支持。用户可供选择的数据库访问技术包括ODBC(Open DataBase Connectivity)、MFC ODBC(Microsoft Foundation Classes ODBC)、DAO (Data Access Object)、OLE DB(Object Link and Embedding DataBase)、ADO(ActiveX Data Object)。这些技术都有各自的特点,适用范围也不尽相同,所以用户一般需要根据技术的特点和项目的特征进行恰当的选择。
1、ODBC
优点:ODBC是客户应用程序访问关系数据库时提供的一个统一的接口,ODBC提供了一套统一的API应对不同的数据库访问,这样使应用程序可以只针对ODBC的API来进行程序编写,就可访问任何提供了ODBC驱动程序的数据库。此外,ODBC还是一种业界标准,几乎所有的关系数据库都提供了相应的ODBC驱动程序,所以具有广泛的应用。
缺点:不过由于ODBC只能用于关系型数据库,使得ODBC很难访问对象数据库及其它非关系型数据库。而且由于ODBC是一个接口层,需要为各种不同的数据库提供适应性,必然会使效率有所降低。此外,在使用ODBC时需要向系统注册一个数据源,这增加一定的配置难度。还需要通过大量的ODBC统一接口API来对数据库访问,也提高了一定的开发难度。
2、MFC ODBC
优缺点:因为MFC ODBC只是通过MFC类库MFC ODBC对ODBC的API进行了一层简单封装,使API更容易被使用,所以它的优缺点就跟ODBC一样,只是降低了一定的开发难度。
3、DAO
优点:DAO(数据访问对象)是一种应用程序编程接口(API),它是微软提供的用于访问Microsoft Jet数据库文件(*.mdb)的强有力的数据库开发工具。DAO是微软的第一个面向对象的数据库接口,各个DAO对象协同工作。通过Jet函数,它还可以访问其他的结构化查询语言(SQL)数据库。因为DAO直接内嵌在ACCESS运行环境中,DAO是访问mdb文件有最高效率的数据库引擎。MFC也提供了一组DAO类,封装了底层的API,而且VC向导也提供了对DAO的支持,从而大大简化了程序的开发。此外不必在ODBC管理器中注册DSN。
缺点:DAO是基于Microsoft Jet引擎的,只能访问Jet引擎支持的桌面数据库。所以要访问Sql Server必须通过ODBC来进行访问。
4、OLE DB
优点:OLE DB是Visual C++开发数据库应用中提供的新技术。OLE DB对所有的文件系统包括关系数据库和非关系数据库都提供了统一的接口。所以使用OLE DB可以对大部分数据库进行广泛的支持。同时它是低级应用程序接口,所以在效率上比ODBC高。
缺点:直接使用OLE DB来设计数据库应用程序需要大量的代码。即便可以通过ATL模板来减少一定的工作量,其开发难度也是相对较大的。可以说,他是所有数据库编程接口中难度最大的。
5、ADO
优点:ADO是一种面向对象的编程接口,它向我们提供了一个熟悉的,高层的对OLE DB的Automation封装接口。它继承了OLE DB技术的优点,具有易于使用、访问效率高、功能强大的特点。因为是面向对象的,内部通过各个对象相互作用实现。既可访问关系型数据库,也可访问非关系型数据库。
缺点:唯一缺点应该是基于COM技术,所以不能跨平台使用,只能用在支持COM接口的机器上,也就是微软的视窗系统了。对于使用VC++来进行开发的话,也不能算是一个缺点了。
http://www.metsky.com/archives/231.html
演进路标(从早到晚):ODBC——OLE DB——DAO——RDO——ADO;
OSI模型层次看(非绝对,部分有交叉):最底层数据源——ODBC——OLE DB——RDO、DAO、ADO——应用程序。
一、ODBC
ODBC全称Open Database Connectivity,ODBC建立一组规范(标准),并提供一组对数据库访问的标准API函数接口。ODBC包括ODBC管理器、ODBC API、ODBC驱动管理器、ODBC数据库驱动管理器四个部分。ODBC可以访问应用程序用户具有其ODBC驱动程序的任何本地或远程数据源,一般是以SQL Server、Oracle等关系数据库作为访问对象。如果使用的是基于Microsoft Jet(.mdb)的数据库,使用下面的ADO比Microsoft Access ODBC驱动程序更有效。
二、OLEDB
OLE DB是基于COM技术的一组接口规范,是系统级数据访问接口,OLE DB提供通用数据访问的方式,不管数据是以何种形式存储,可以对电子邮件、文本文件、复合文件、数据表等各种各样的数据通过统一的接口进行存取的一种技术。用来访问各种数据源,如ORACLE、SQL Server、Access、Excel等等。而且相比下文的ADO通用数据访问方 式,OLE DB性能要更强一点。
三、DAO
DAO全称DATA ACCESS OBJECTS,是基于MICROSOFT的JET 技术设计,面向对象的数据访问接口,最早在VB中使用,DAO可以访问的数据库有:
1、用数据库引擎版本为 1.x、2.x 和 3.0 的 Microsoft Access 或 Microsoft Visual Basic 创建的、使用 Microsoft Jet 数据库引擎的数据库。
2、可安装的ISAM 数据库,包括:dBASE III、dBASE IV、dBASE 5.0、Paradox 3.x、4.x 和 5.x 版。
3、开放式数据库连接(ODBC)数据库,包括但不仅限于Microsoft SQL Server、SYBASE SQL Server 和 ORACLE Server。若要访问ODBC数据库,必须具有希望访问的数据库的适当ODBC驱动程序。
4、Microsoft Excel 3.0、4.0、5.0 和 7.0 版工作表。
5、Lotus WKS、WK1、WK3 和 WK4 电子表格。
6、文本文件。
四、RDO
RDO全称Remote Data Objects,翻译就是远程数据对象访问,以ODBC为基础,依赖ODBC API、选定的ODBC 驱动程序以及后端数据库引擎实现大部分的智能和功能,RDO具备基本的ODBC处理方法,所以可以直接执行大多数ODBC API函数。RDO是从DAO派生的,同DAO最大的不同在于其数据库处理模式。DAO是针对Records和Fields,而RDO是按照Rows和Columns来处理。也就是说DAO是ISAM模式,RDO是关系模式。此外DAO是访问Access的Jet引擎 (Jet是ISAM)的接口,而RDO则是访问ODBC的接口。可见,RDO是综合了DAO/Jet、ODBC等优点。
五、ADO
ADO(ACTIVEX DATA OBJECTS)——ADO是基于OLE DB(ActiveX技术)技术设计的应用层数据访问接口,对OLE DB接口进行封装,所以使用中仍然是通过OLE DB桥接数据库。所以,ADO同OEL DB一样提供了通用数据访问功能,支持关系数据库和非关系数据库的访问。ADO功能上对DAO和RDO进行了综合演绎,就是说有合并也有取消优化等等。
http://www.metsky.com/archives/232.html
1、在StdAfx.h中引入ADO类支持
方法是在StdAfx.h中增加下面两句话:
#include <comdef.h>//如果需要则添加本句
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename ("EOF", "adoEOF")
2、初始化COM环境
在MFC中可以用AfxOleInit()或CoInitialize(NULL),该函数一般放在InitInstance()历程里。
非MFC使用用CoInitialize(NULL)。
卸载COM环境使用CoUnInitialize(),一般放在主程序的析构函数里。
这样我们就会三个指针可用:_ConnectionPtr、_RecordsetPtr和_CommandPtr,分别代表:
_ConnectionPtr接口返回一个记录集或一个空指针,通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,对于要返回记录的操作通常使用_RecordserPtr来实现。而用 _ConnectionPtr操作时要想得到记录条数得遍历所有记录,用_RecordserPtr则不需要。
_RecordsetPtr指针是一个记录集对象。可以对记录集提供了更多的控制功能。它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给 _RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是使用已经创建了数据库连接的全局_ConnectionPtr接口,然后使用_RecordsetPtr执行存储过程和SQL语句。
_CommandPtr指针接口返回一个记录集。CommandPtr提供了一种简单方法来执行返回记录集的存储过程和SQL语句。可以利用全局 _ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。一次或少量数据库访问操作,一般是直接使用连接串,如果需要频繁访问数据库,涉及返回多个记录集,那么,建议同_RecordsetPtr用法一样,使用全局数据库连接后,再使用_CommandPtr 接口执行存储过程和SQL语句。
各指针的定义方法:
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
_CommandPtr m_pCommand;
3、连接、关闭数据库
1、连接数据库
示例连接ACCESS,以昨天的FavorMan为例。
::CoInitialize(NULL);//数据库操作前先初始化COM环境
CString strSQL; //配置初始连接串
strSQL="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=blogurl.mdb;";
strSQL=strSQL+"Provider=Microsoft.Jet.OLEDB.4.0;"+"Data Source=blogurl.mdb;"+
"Jet OLEDB:Database Password="+//str为上面读到数据库路径信息!
DB_PASSWRD_STRING+";"+
"Persist Security Info=False;"; //注意一定需要输入四个\\\\才能表示"\\"
//如果单独输入,必须形式为"\\\\Abc\\db\\blogurl.mdb"格式!
//--连接数据库-----------------
HRESULT hr;
try
{
hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
if(SUCCEEDED(hr))
{
hr = m_pConnection->Open((_bstr_t)strSQL,"","",adModeUnknown);///连接数据库
///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51; }
}
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误:%s!",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
return FALSE;
}
2、关闭数据库
if( m_pConnection->State )//如果连接有效
m_pConnection->Close();
m_pConnection = NULL;
4、数据库访问
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance("ADODB.Recordset");
try
{
//打开数据库
m_pRecordset->Open("SELECT * FROM BlogUrl",
m_pConnection.GetInterfacePtr(),//或使用_variant_t((IDispatch*)theApp.m_pConnection,true),,但需要extern声明theApp;
adOpenDynamic,
adLockOptimistic,
adCmdText);
//遍历读取
while(!m_pRecordset->adoEOF)//adoEOF判断数据库指针是否已经到结果集末尾;BOF判断是否在第一条记录前面
{
vID =m_pRecordset->GetCollect("ID");
//这里已经读到当前记录的ID,需要进行非空等判断,非空之后就可以处理,比如添加到列表框等。
//....
m_pRecordset->MoveNext();
}
m_pRecordset->Close();//关闭记录集
}
catch(_com_error error)
{
CString errorMessage;
errorMessage.Format("%s",(LPTSTR)error.Description());
AfxMessageBox(errorMessage);
}
取得某字段的值有两种办法,一种是指定该字段名,另一种是指定该字段的ID编号,从0开始。
假设上文的ID是从0开始,那么可以采用:
m_pRecordset->GetCollect(_variant_t(long(0));
也可以采用:
pRecordset->get_Collect("ID");
其它索引值读取类似处理。
附一些常用的执行语法:
1、添加记录
a、调用m_pRecordset->AddNew();
b、调用m_pRecordset->PutCollect("ID",vID);给ID字段赋值
c、调用m_pRecordset->Update();//确认并刷新入库
2、修改记录
方法类似添加记录,只是起初按照查询指定记录打开接口,比如SELECT * FROM ulist where ID=%d形式,不需要m_pRecordset->AddNew();,直接使用PutCollect("ID",vID)。
3、删除记录
删除记录只需要把记录指针移动到要删除的记录上,然后调用m_pRecordset->Delete(adAffectCurrent)
移动到指定记录上也有两种方法,一种直接使用查询指定记录(需要唯一字段)方式定位,另一种是使用Move(index)方式。
下面例子是使用Move方式到指定记录。
try
{
m_pRecordset->MoveFirst();//如果是采用SELECT * FROM ulist where ID=5方式打开数据库连接则不需要本句
m_pRecordset->Move(5);//假设删除第5条记录,如果是采用SELECT * FROM ulist where ID=5方式打开数据库连接则不需要本句
m_pRecordset->Delete(adAffectCurrent);
m_pRecordset->Update();
}
catch(_com_error error)
{
CString errorMessage;
errorMessage.Format("%s",(LPTSTR)error.Description());
AfxMessageBox(errorMessage);
}
4、_CommandPtr用法参考
a、_CommandPtr指针初始化
m_pCommand->ActiveConnection = m_pConnection;// 将库连接赋于它,或单独再创建也可以,根据实际需要定
m_pCommand->CommandText = "SELECT * FROM BlogUrll";
b、_CommandPtr与_RecordsetPtr配合读取记录集
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText);//执行SQL语句,返回结果记录集
c、直接使用_ConnectionPtr执行SQL语句
m_pConnection->Execute( _bstr_t CommandText,VARIANT * RecordsAffected,long Options )
例如:
m_pConnection->Execute("UPDATE BlogUrl SET ID= 1 WHERE ID = 5",&RecordsAffected,adCmdText);
5、遍历数据库中所有表名
m_pRecordset = m_pConnect->OpenSchema(adSchemaTables);
while(!(m_pRecordset ->adoEOF))
{
_bstr_t tblname = m_pRecordset->Fields->GetItem("TABLE_NAME")->Value;//获取表格
_bstr_t tbltype = m_pRecordset->Fields->GetItem("TABLE_TYPE")->Value;//获取表格类型
//这里可以对表格类型进行判断,判断后即可处理tblname
if (!strcmp(tbltype ,"TABLE"))
{
AfxMessageBox(tblname);
}
m_pRecordset->MoveNext();
}
m_pRecordset->Close();
6、遍历一个表中的所有字段
//方法1:
m_pRecordset = m_pConnect->OpenSchema(adSchemaColumns);
while(!(m_pRecordset ->adoEOF))
{
_bstr_t colname = m_pRecordset->Fields->GetItem("COLUMN_NAME")->Value;//获取字段名
AfxMessageBox(colname);
m_pRecordset->MoveNext();
}
m_pRecordset->Close();
//方法2:
FieldsPtr pFields = NULL;
HRESULT hr = m_pRecordset->get_Fields (&pFields);//得到记录集的字段集
if(!SUCCEEDED(hr)) return;
int iColCount;
pFields->get_Count(&iColCount);//字段总数
BSTR bstrColName;
for(int i=0;i<iColCount;i++)//按记录集的各字段循环
{
//pFields->GetItem(_variant_t(i))->get_Name(&bstrColName);
pFields->Item(_variant_t(i))->get_Name(&bstrColName);//获得字段名字
CString csName=bstrColName;
//pField->Item[i]->get_Type(&fType);//获取字段类型
//pField->Item[i]->get_DefinedSize(&lSize);//获得字段的大小
AfxMessageBox(csName);
}
if(NULL != hr)
tblfields ->Release();//释放指针
7、常用数据格式转换
_variant_t var;
BSTR bvar;
CString csVar;
_bstr_t bstr;
_variant_t 转化为long型: (long)var;
_variant_t转化为 CString型: csVar= (LPCSTR)_bstr_t(var);
CString转化为_variant_t型: _variant_t(csVar);或(LPTSTR)(LPCTSTR)csVar;
CString转化为BSTR型: bvar= csVar.AllocSysString();
BSTR转化为CString型:csVar= (LPCSTR)bvar;
CString转化为_bstr_t型: bstr = (_bstr_t)csVar;
_bstr_t 转化为CString型:csVar= (LPCSTR)bstr;