今天对ADO学了一下,最开始也遇到了很多的麻烦,不过后来看了网上的很多优秀的资料,学到了很多的东西,在此想总结一下,希望对有共同需求的人有一些帮助:
ADO(Active Data Object,活动数据对象),实际上是一种基于COM(组件对象模型)的自动化接口(IDispatch)技术,并以OLE DB(对象连接和镶入的数据库)为基础,经过OLE DB精心包装后的数据库访问技术,利用它可以快速的创建数据库应用程序。 ADO提供了一组非常简单,将一般通用的数据访问细节进行封装的对象。由于ODBC数据源也提供了一般的OLE DB Privider,所以ADO不仅可以应用自身的OLE DB Privider,而且还可以应用所有的ODBC驱动程序。下面讲讲具体开发流程:
1.导入ADO类
#import "c:/program files/common files/system/ado/msado15.dll" /
no_namespace /
rename ("EOF", "adoEOF")
在这里, 路径名可以根据自己系统安装的ADO支持文件的路径来自行设定。当编译器遇到#import语句时,它会为引用组件类型库中的接口生成包装类,#import语句实际上相当于执行了API涵数LoadTypeLib()。#import语句会在工程可执行程序输出目录中产生两个文件,分别为*.tlh(类型库头文件)及*.tli(类型库实现文件),它们分别为每一个接口产生智能指针,并为各种接口方法、枚举类型,CLSID等进行声明,创建一系列包装方法。语句no_namespace说明ADO对象不使用命名空间,rename ("EOF", "adoEOF")说明将ADO中结束标志EOF改为adoEOF,以避免和其它库中命名相冲突。在下面我们就会用到adoEOF
2.初始化COM
导入了ADO的com组件,还必须得对其初始化,完了后还得关闭它,如下:
CoInitialize(NULL); //初始化
CoUninitialize(); //关闭
有人说,在MFC中可以用一句话执行同样的操作,如下:
AfxOleInit(); //不过我没有试成功过,不知道是什么原因,所以最好还是用上面的那种方法吧,呵呵。
接着,就可以直接使用ADO的操作了。我们经常使用的只是前面用#import语句引用类型库时,生成的包装类.tlh中声明的智能指针中的三个,它们分别是_ConnectionPtr、_RecordsetPtr和_CommandPtr。下面分别对它们的使用方法进行介绍
3.打开数据库
_ConnectionPtr m_pConnection;//首先声明变量:
m_pConnection.CreateInstance(__uuidof(Connection)); //实例化
try
{
// 打开数据库
_bstr_t connectSetting="Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=CarInfo;Data Source=TR-LILAI";
m_pConnection->Open(connectSetting,"","",adModeUnknown); }
catch(_com_error e)
{
AfxMessageBox(L"数据库连接失败,确认数据库是否在当前路径下!");
return FALSE;
}
打开数据库的原型
HRESULT Open (
_bstr_t ConnectionString, //连接字符串
_bstr_t UserID, //用户号
_bstr_t Password, //用户密码
long Options ); //连接模式
其中最重要的,也是最容易出错的就数 ConnectionString了,
provider:表示数据提供者,就是我们的数据库引擎
initial Catalog:数据库名,也可以使用database
DataSource:数据来源,就是服务器名
详细的参数说明请见:
http://blog.csdn.net/kable999/archive/2009/07/29/4391325.aspx
4.关闭数据库
在不使用数据库的时候,则关闭库连接。如果连接状态有效,则用Close方法关闭它并赋于它空值。代码如下所示:
if(m_pConnection->State)
m_pConnection->Close();
m_pConnection= NULL;
5.使用结果集(_RecordsetPtr)
_RecordsetPtr可以用来打开库内数据表,并可以对表内的记录、字段等进行各种操作。
>打开数据表。
首先创建一个_RecordsetPtr实例,然后调用Open()得到一条SQL语句的执行结果,如下:
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));//创建实例,大家可以看到其实就是一个RecordSet
// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
Try
{
m_pRecordset->Open("SELECT * FROM TestTable", // 查询TestTable表中所有字段
theApp.m_pConnection.GetInterfacePtr(), // 获取库接库的IDispatch指针
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
//其他异常处理
}
Open方法的原型:
HRESULT Recordset15::Open (
const _variant_t & Source, //SQL查询语句
const _variant_t & ActiveConnection, //指定已经建立好的连接
>关闭记录集。
直接用Close方法关闭记录集并赋于其空值。代码如下所示:
m_pRecordset->Close();
m_pRecordset = NULL;
>读记录集
还记得前面的“ rename ("EOF", "adoEOF")”吗?这里派上用场了,如下:
CString strName;
while(!m_pRecordset->adoEOF) //到记录集末尾了吗?
{
_variant_t var = m_pRecordset->GetCollect("Name"); //获取Name列的值
if(var.vt != VT_NULL)
strName = (LPCSTR)_bstr_t(var); //_variant_t 转化为CString
m_pRecordset->MoveNext(); //游标下移一位
}
GetCollect的原型
inline _variant_t Recordset15::GetCollect ( const _variant_t & Index )
index: 是一个_variant_t 类型,在这里除了使用列名外,我们也可以使用索引,如下:
m_pRecordset->GetCollect(_variant_t(long(0));
>插入记录
可以先用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条记录位置,move(1)与movenext相同。
>修改记录
可以将记录指针移动到要修改记录的位置处,直接用PutCollect(字段名,值)将新值写入,最后别忘了Update()一下下哦。
如下:
try
{
// 假设对第一条记录进行修改
m_pRecordset->MoveFirst();
m_pRecordset->PutCollect("Name", _variant_t(m_Name));
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
>删除记录
删除记录和上面修改记录的操作类似,先将记录指针移动到要修改记录的位置,直接用Delete()方法删除它并用Update()来更新数据库既可。部分代码如下所示:
....................
m_pRecordset->Delete(adAffectCurrent); // 参数adAffectCurrent为删除当前记录
m_pRecordset->Update();
.................
6.执行SQL语句
其实处理结果集,我们还有很多方式执行SQL语句。
>用_CommandPtr和_RecordsetPtr配合
_CommandPtr m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection; // 将库连接赋于它
m_pCommand->CommandText = "SELECT * FROM TestTable"; // SQL语句
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText); // 执行SQL语句,返回记录集
>直接用_ConnectionPtr执行
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText,
VARIANT * RecordsAffected,
long Options )
其中CommandText是命令字串,通常是SQL命令。
参数RecordsAffected是操作完成后所影响的行数,
参数Options表示CommandText中内容的类型,Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知
例子:
variant_t RecordsAffected;
m_pConnection->Execute(
"UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
7.执行存储过程
执行存储过程的操作和上面执行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)
//或者直接用_ConnectionPtr调用
variant_t RecordsAffected;
m_pConnection->Execute( "Demo",&RecordsAffected,adCmdStoredProc);
8.遍历数据库中的所有表名
_ConnectionPtr m_pConnect;
_RecordsetPtr pSet;
HRESULT hr;
try
{
hr = m_pConnect.CreateInstance("ADODB.Connection");
if(SUCCEEDED(hr))
{
CString dd;
dd.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s",file);
hr = m_pConnect->Open((_bstr_t)dd,"","",adModeUnknown);
pSet = m_pConnect->OpenSchema(adSchemaTables);
while(!(pSet->adoEOF))
{
//获取表格
_bstr_t table_name = pSet->Fields->GetItem("TABLE_NAME")->Value;
//获取表格类型
_bstr_t table_type = pSet->Fields->GetItem("TABLE_TYPE")->Value;
//过滤一下,只输出表格名称,其他的省略
if ( strcmp(((LPCSTR)table_type),"TABLE")==0){
CString tt;
tt.Format("%s",(LPCSTR)table_name);
AfxMessageBox(tt);
}
pSet->MoveNext();
}
pSet->Close();
}
m_pConnect->Close();
}catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!rn错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return -1;
}
9.遍历一个表中的所有字段
Field * field = NULL;
HRESULT hr;
Fields * fields = NULL;
hr = m_pRecordset->get_Fields (&fields); //得到记录集的字段集和
if(SUCCEEDED(hr))
fields->get_Count(&ColCount);
//得到记录集的字段集合中的字段的总个数
for(i=0;iItem[i]->get_Name(&bstrColName); //得到记录集//中的字段名
strColName=bstrColName;
nameField = strColName;
m_FieldsList.AddString(nameField);
}
if(SUCCEEDED(hr))
fields->Release();//释放指针
其他:
1、_variant_t
(1)、一般传给这3个指针的值都不是MFC直接支持的数据类型,而要用_variant_t转换一下
_variant_t(XX)可以把大多数类型的变量转换成适合的类型传入:
(2)、_variant_t var;
_variant_t -> long: (long)var;
_variant_t -> CString: CString strValue = (LPCSTR)_bstr_t(var);
CString -> _variant_t: _variant_t(strSql);
2、BSTR宽字符串与CString相互转换
BSTR bstr;
CString strSql;
CString -> BSTR: bstr = strSql.AllocSysString();
BSTR -> CString: strSql = (LPCSTR)bstr;
3、_bstr_t与CString相互转换
_bstr_t bstr;
CString strSql;
CString -> _bstr_t: bstr = (_bstr_t)strSql;
_bstr_t -> CString: strSql = (LPCSTR)bstr;
4、关于时间
Access:表示时间的字符串#2004-4-5#
Sql:表示时间的字符串''2004-4-5''
DateField(时间字段) select * from my_table where DateField > #2004-4-10#
参考资料:
http://www.vckbase.com/document/viewdoc/?id=1215
http://hi.baidu.com/%C0%BC%D7%CF%F6%AA/blog/item/62c345de14ad671b6227985f.html
http://blog.csdn.net/b87548116/archive/2009/08/17/4454699.aspx
http://hi.baidu.com/kxkboy/blog/item/5b0553d84461af2510df9bf6.html
http://hi.baidu.com/15971/blog/item/2418131275442655f819b8b0.html