_ConnectionPtr
接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用 _ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr来实现。而用 _ConnectionPtr操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr时不需要。
_CommandPtr
接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局 _ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记录集,那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用_CommandPtr 接口执行存储过程和SQL语句。
_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同_CommandPtr接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给 _RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口,然后使用_RecordsetPtr执行存储过程和SQL语句。
(1)、引入ADO类
1
2
3
| #import "c:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename ("EOF", "adoEOF")
|
(2)、初始化COM
在MFC中可以用AfxOleInit();非MFC环境中用:
CoInitialize(NULL);
CoUnInitialize();
(3)#import 包含后就可以用3个智能指针了
:_ConnectionPtr、_RecordsetPtr和_CommandPtr
1.连接和关闭数据库
(1)连接
例子:连接Access数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| AfxOleInit();//初始化
HRESULT hr;
try
{
//hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
hr = m_pConnection.CreateInstance(_uuidof(Connection));///创建Connection实例 if(SUCCEEDED(hr))
{
m_pConnection->ConnectionTimeout = 0;
hr = m_pConnection->Open( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb", "", "", adModeUnknown);
//m_pConnection->PutDefaultDatabase ((_bstr_t)"DB");//设置默认数据库
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->CommandTimeout = 5;
m_pCommand->ActiveConnection = m_pConnection;
}
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
} |
(2)、关闭
1
2
3
4
5
6
7
8
9
| //如果数据库连接有效
if( m_pConnection->State )
m_pConnection->Close();
m_pConnection = NULL;
<span style="color: #ff0000;">(3)、设置连接时间
</span>
//设置连接时间-----------------------------------
pConnection->put_ConnectionTimeout(long(5));
|
2.打开一个结果集
(1)打开,首先创建一个_RecordsetPtr实例,然后调用Open()得到一条SQL语句的执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| _RecordsetPtrm_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));
// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。jingzhou xu
try
{
m_pRecordset->Open("SELECT * FROM DemoTable",// 查询DemoTable表中所有字段
m_pConnection.GetInterfacePtr(), // 获取库接库的IDispatch指针
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
|
(2)关闭结果集
m_pRecordset->Close();
3.操作一个结果集
(1)、遍历(读取)
a)、用pRecordset->adoEOF来判断数据库指针是否已经移到结果集的末尾了;m_pRecordset->BOF判断是否 在第一条记录前面:
1
2
3
4
5
6
7
8
9
10
11
| 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();
}
|
b)、取得一个字段的值的办法有两种办法
一是
//表示取得第0个字段的值 m_pRecordset->GetCollect(“Name”);
或者 m_pRecordset->GetCollect(_variant_t(long(0));
二是
pRecordset->get_Collect(“COLUMN_NAME”);
或者 pRecordset->get_Collect(long(index));
(2)、添加
a)、调用m_pRecordset->AddNew();
b)、调用m_pRecordset->PutCollect();给每个字段赋值
c)、调用m_pRecordset->Update();确认
(3)、修改
(4)、删除
a)、把记录指针移动到要删除的记录上,然后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| Delete(adAffectCurrent) try
{
// 假设删除第二条记录
m_pRecordset->MoveFirst();
m_pRecordset->Move(1);
// 从0开始
m_pRecordset->Delete(adAffectCurrent);
// 参数adAffectCurrent为删除当前记录
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
|
4.直接执行SQL语句,除了要用到结果集其余的大部分功能都可以直接用SQL语言实现
(1)、用_CommandPtr和_RecordsetPtr配合
1
2
3
4
5
6
7
8
9
| _CommandPtrm_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
// 将库连接赋于它
m_pCommand->ActiveConnection = m_pConnection;
// SQL语句
m_pCommand->CommandText = "SELECT * FROM DemoTable";
// 执行SQL语句,返回记录集
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText);
|
(2)、直接用_ConnectionPtr执行SQL语句
1
2
3
| _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);
5.调用存储过程
(1)、利用_CommandPtr
1
2
3
4
5
| _CommandPtrm_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection; // 将库连接赋于它
m_pCommand->CommandText = "Demo";
m_pCommand->Execute(NULL,NULL, adCmdStoredProc);
|
(2)、直接用_ConnectionPtr直接调用(见4.(2))
6.遍历数据库中的所有表名
_ConnectionPtr m_pConnect;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| _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;
}
|
7.遍历一个表中的所有字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| 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#
1
2
3
4
5
6
7
8
9
10
11
| try
{
m_pCommand->CommandText = "INSERT INTO tTest(age) VALUES('23f2') ";
m_pRecordset = m_pCommand->Execute(NULL,NULL, adCmdText);
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
}
|
来源:http://blog.csdn.net/umbrella1984/archive/2005/05/29/383628.aspx
_RecordsetPtr m_pRecordset;//创建一个_RecordsetPtr m_pRecordset.CreateInstance("ADODB.Recordset"); //创建一个实例 try { m_pRecordset->Open(
"SELECT * FROM duty", //sql查询语句
m_pConnection.GetInterfacePtr(), //得到sql连接的指针
//Const adOpenDynamic = 2 '动态游标功能最强,但耗资源也最多。用户对记录说做的修改,增加或删除记录都将反映到记录集中。支持全功能浏览(ACCESS不支持)。
adOpenDynamic,
//Const adLockOptimistic = 3 '只有在调用Update方法时才锁定记录集,而在此前的其他操作仍可对当前记录进行更改、插入和删除 等
adLockOptimistic,
//AdCmdText 将 CommandText 作为命令或存储过程调用的文本化定义进行计算。
adCmdText);
}
catch(_com_error e)
{
//cout<<e->ErrorMessage()<<endl;
AfxMessageBox("Create Instance failed!");
return;
}
内容一:
'定义数据库连接的一些常量
Const adOpenForwardOnly = 0 '(默认值)游标只向前浏览记录,不支持分页、Recordset、BookMark
Const adOpenKeyset = 1 '键集游标,其他用户对记录说做的修改将反映到记录集中,但其他用户增加或删除记录不会反映到记录集中。支持分页、Recordset、BookMark
Const adOpenDynamic = 2 '动态游标功能最强,但耗资源也最多。用户对记录说做的修改,增加或删除记录都将反映到记录集中。支持全功能浏览(ACCESS不支持)。
Const adOpenStatic = 3 '静态游标,只是数据的一个快照,用户对记录说做的修改,增加或删除记录都不会反映到记录集中。支持向前或向后移动
Const adLockReadOnly = 1 '(默认值)锁定类型,默认的,只读,不能作任何修改
Const adLockPessimistic = 2 '当编辑时立即锁定记录,最安全的方式
Const adLockOptimistic = 3 '只有在调用Update方法时才锁定记录集,而在此前的其他操作仍可对当前记录进行更改、插入和删除等
Const adLockBatchOptimistic = 4 '当编辑时记录不会被锁定,而更改、插入和删除是在批处理方式下完成的
Const adCmdText = &H0001
Const adCmdTable = &H0002
%>
Open 方法 (ADO Recordset)
打开游标。
语法
recordset.Open Source, ActiveConnection, CursorType, LockType, Options
内容二:
CommandType 属性
指示 Command 对象的类型。
设置和返回值
设置或返回以下某个 CommandTypeEnum 值。
常量 说明
AdCmdText 将 CommandText 作为命令或存储过程调用的文本化定义进行计算。
AdCmdTable 将 CommandText 作为其列全部由内部生成的 SQL 查询返回的表格的名称进行计算。
AdCmdTableDirect 将 CommandText 作为其列全部返回的表格的名称进行计算。
AdCmdStoredProc 将 CommandText 作为存储过程名进行计算。
AdCmdUnknown 默认值。CommandText 属性中的命令类型未知。
adCmdFile 将 CommandText 作为持久 Recordset 文件名进行计算。
AdExecuteNoRecords 指示 CommandText 为不返回行的命令或存储过程(例如,插入数据的命令)。如果检索任意行,则将丢弃这些行且并不返回。它总是与 adCmdText 或 adCmdStoredProc 进行组合。
说明
使用 CommandType 属性可优化 CommandText 属性的计算。
如果 CommandType 属性的值等于 adCmdUnknown(默认值),系统的性能将会降低,因为 ADO 必须调用提供者以确定 CommandText 属性是 SQL 语句、还是存储过程或表格名称。如果知道正在使用的命令的类型,可通过设置 CommandType 属性指令 ADO 直接转到相关代码。如果 CommandType 属性与 CommandText 属性中的命令类型不匹配,调用 Execute 方法时将产生错误。
adExecuteNoRecords 常量通过最小化内部处理来提高性能。该常量不独立使用,它总是与 adCmdText 或 adCmdStoredProc 组合(如adCmdText+adExecuteNoRecords) 一起使用。如果与 Recordset.Open 一起使用 adExecuteNoRecords,或者该方法使用 Command 对象都将产生错误。
VC++一般注意点(ADO类)
1. 关于ADO智能指针的释放问题:
在释放ADO智能指针 _ConnectionPtr 和_RecordsetPtr的时候要注意:首先要判断它们是否是处于Open状态,是,则先close再判断指针是否为空,是,则release释放,否则跳过;
例:
_ConnectionPtr m_pOwnConn;
_RecordsetPtr m_pCmdRs;
……
……
//关闭,释放智能指针
//若已经close了再close会崩溃
if(m_pCmdRs->State==adStateOpen)
{
m_pCmdRs->Close();
}
if (m_pCmdRs)
{
m_pCmdRs.Release();
m_pCmdRs = NULL;
}
// if(m_pOwnConn->GetStatus())
if (m_pOwnConn->GetState() == adStateOpen)
{
m_pOwnConn->Close();
}
if (m_pOwnConn)
{
m_pOwnConn.Release();
m_pOwnConn = NULL;
}
注:
1.1 m_pConnection对象还没有产生,就使用其成员state,必然会产生非法访问错误
1.2 _ConnectionPtr m_pConneciton;
if(m_pConnection->state)//如果关闭了是0,打开是1
///
上叙述方法是不行的。我也遇到过这样的问题,而且这样处理的。但测试的时候发现,一但连接上数据库后,把网线拔了也就是说让他和数据库的连接中断了,跟踪state的状态一直为1。我也没能找到原因。
/
我的解决办法是用了一个替代办法,在进行任何操作之前,我去检索一个肯定存在在数据库中的表,如果操作成功,说明连接正常。否则从新连接,该程序到现在运行一直很正常。
1.3 用异常处理,捕获连接断开的错误代码。可以判断连接是否断开。
2. 关于”_CommandPtr无效指针”
在编译机(winxp sp2 +office2003 + vc6sp6)上执行程序没有问题,而在测试机(win2000 +office2000?)上招待程序出现上面说的异常.
程序里导入ADO库如下:
//#import "C:\Program Files\Common Files\System\ADO\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
测试机WIN2000,ado的版本是2.5的,而编译机上的却是2.8版本的
所以在编译机上把2.8版本的导入后,_CommandPtr用的是新版本的接口,旧版本2.5的就无法认出了.
解决方法
1).把一个版本足够低的ADO版本比如说msado15.dll是2.5XX版本的,放到编译机上,和程序一起编译.
2).在测试机上装MDAC
3. 关于”_CommandPtr”指针创建失败的原因
1) 没在头文件加入ADO动态库,例:
#pragma warning(disable:4146)
#import "c:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" named_guids rename("EOF","adoEOF"), rename("BOF","adoBOF")
#pragma warning(default:4146)
using namespace ADODB;//如果加了这句,就不用在代码中再加空间域名前缀了
2) 由于引用的ADO库,是COM库操作,所以得先初始化COM操作.
//每个用动COM组件的都要初始化COM库和卸载COM库
HRESULT hr = CoInitialize( NULL);
if ( FAILED( hr ) )
{
…
}
::CoUninitialize();
Reference:
学习ADO总结