加入收藏 | 设为首页 | 会员中心 | 我要投稿 湖南网 (https://www.hunanwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 电商 > 正文

Visual C++ ADO数据库编程入门(上)

发布时间:2018-08-19 03:31:37 所属栏目:电商 来源:站长网
导读:ADO 是今朝在Windows情形中较量风行的客户端数据库编程技能。ADO是成立在OLE DB底层技能之上的高级编程接口,因而它兼具有强盛的数据处理赏罚成果(处理赏罚各类差异范例的数据源、漫衍式的数据处理赏罚等等)和极其简朴、易用的编程接口,因而获得了普及的应用。并且
   ADO 是今朝在Windows情形中较量风行的客户端数据库编程技能。ADO是成立在OLE DB底层技能之上的高级编程接口,因而它兼具有强盛的数据处理赏罚成果(处理赏罚各类差异范例的数据源、漫衍式的数据处理赏罚等等)和极其简朴、易用的编程接口,因而获得了普及的应用。并且按微软公司的意图,OLE DB和ADO将慢慢代替 ODBC和DAO。此刻先容ADO各类应用的文章和书本有许多,本文着重站在初学者的角度,扼要切磋一下在VC++中行使ADO编程时的一些题目。我们但愿阅读本文之前,您对ADO技能的根基道理有一些相识。

  一、在VC++中行使ADO编程

  ADO现实上就是由一组Automation工具组成的组件,因此可以象行使其余任何Automation工具一样行使ADO。ADO中最重要的工具有三个:Connection、Command和Recordset,它们别离暗示毗连工具、呼吁工具和记录集工具。假如您认识行使MFC中的ODBC类(CDatabase、CRecordset)编程,那么进修ADO编程就异常轻易了。

  行使ADO编程时可以回收以下三种要领之一:

  1、行使预处理赏罚指令#import

#import "C:Program FilesCommon Files ystemADOmsado15.dll"
no_namespace rename("EOF", "EndOfFile")
  但要留意不能放在stdAfx.h文件的开头,而应该放在全部include指令的后头。不然在编译时会堕落。
措施在编译进程中,VC++会读出msado15.dll中的范例库信息,自动发生两个该范例库的头文件和实现文件msado15.tlh和msado15.tli(在您的Debug或Release目次下)。在这两个文件里界说了ADO的全部工具和要领,以及一些列举型的常量等。我们的措施只要直接挪用这些要领就行了,与行使MFC中的COleDispatchDriver类挪用Automation工具异常相同。

  2、行使MFC中的CIDispatchDriver

  就是通过读取msado15.dll中的范例库信息,成立一个COleDispatchDriver类的派生类,然后通过它挪用ADO工具。

  3、直接用COM提供的API

  如行使如下代码:

CLSID clsid;
HRESULT hr = ::CLSIDFromProgID(L"ADODB.Connection", &clsid);
if(FAILED(hr))
{...}
::CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)
&pDispatch);
if(FAILED(hr))
{...}
  以上三种要领,第一和第二种相同,也许第一种好用一些,第三种编程也许最贫困。但也许第三种要领也是服从最高的,措施的尺寸也最小,而且对ADO的节制手段也最强。

  据微软资料先容,第一种要领不支持要领挪用中的默认参数,虽然第二种要领也是这样,但第三种就不是这样了。回收第三种要领的程度也最高。当你必要绕过ADO而直接挪用OLE DB底层的要领时,就必然要行使第三种要领了。

  ADO编程的要害,就是纯熟地运用ADO提供的各类工具(object)、要领(method)、属性(property)和容器(collection)。其它,假如是在MS SQL或Oracle等大型数据库上编程,还要能纯熟行使SQL说话。


  二、行使#import要领的编程步调

  这里提议您行使#import的要领,由于它易学、易用,代码也较量简捷。

  1、 添加#import指令

  打开stdafx.h文件,将下列内容添加到全部的include指令之后:

#include <icrsint.h> //Include support for VC++ Extensions
#import "C:Program FilesCommon Files ystemADOmsado15.dll"
no_namespace rename("EOF", "adoEOF")
  个中icrsint.h文件包括了VC++扩展的一些预处理赏罚指令、宏等的界说,用于COM编程时行使。

  2、界说_ConnectionPtr型变量,并成立数据库毗连

  成立了与数据库处事器的毗连后,才气举办其他有关数据库的会见和操纵。ADO行使Connection工具来成立与数据库处事器的毗连,以是它相等于MFC中的CDatabase类。和CDatabase类一样,挪用Connection工具的Open要领即可成立与处事器的毗连。

  数据范例 _ConnectionPtr现实上就是由类模板_com_ptr_t而获得的一个详细的实例类,其界说可以到msado15.tlh、comdef.h 和comip.h这三个文件中找到。在msado15.tlh中有:

_COM_SMARTPTR_TYPEDEF(_Collection, __uuidof(_Collection));
  经宏扩展后就获得了_ConnectionPtr类。_ConnectionPtr类封装了Connection工具的Idispatch接口指针,及一些须要的操纵。我们就是通过这个指针来哄骗Connection工具。相同地,后头用到的_CommandPtr和_RecordsetPtr范例也是这样获得的,它们别离暗示呼吁工具指针和记录集工具的指针。

  (1)、毗连到MS SQL Server

  留意毗连字符串的名目,提供正确的毗连字符串是乐成毗连到数据库处事器的第一步,有关系接字符串的具体信息拜见微软MSDN Library光盘。

  本例毗连字符串中的server_name,database_name,user_name和password在编程时都应该替代成现实的内容。

_ConnectionPtr pMyConnect=NULL;
HRESULT hr=pMyConnect.CreateInstance(__uuidof(Connection)));
if(FAILED(hr))return;

_bstr_t strConnect="Provider=SQLOLEDB; Server=server_name;"
"Database=database_name; uid=user_name; pwd=password;";
//connecting to the database server now:
try{pMyConnect->Open(strConnect,"","",NULL);}
catch (_com_error &e)
{
::MessageBox(NULL,e.Description(),"告诫",MB_OK │ MB_ICONWARNING);
}
  留意Connection工具的Open要领中的毗连字符串参数必需是BSTR或_bstr_t范例。其它,本例是直接通过OLE DB Provider成立毗连,以是无需成立数据源。

  (2)、通过ODBC Driver毗连到Database Server毗连字符串名目与直接用ODBC编程时的差不多:

_bstr_t strConnect="DSN=datasource_name; Database=database_name; uid=user_name; pwd=password;";
  此时与ODBC编程一样,必需先成立数据源。

  3、界说_RecordsetPtr型变量,并打开数据集

  界说_RecordsetPtr型变量,然后通过它挪用Recordset工具的Open要领,即可打开一个数据集。以是Recordset工具与MFC中的CRecordset类相同,它也有当前记录、当前记录指针的观念。如:

_RecordsetPtr m_pRecordset;
if(!FAILED(m_pRecordset.CreateInstance( __uuidof( Recordset )))
{
m_pDoc->m_initialized=FALSE;
return;
}

try{
m_pRecordset->Open(_variant_t("mytable"),
_variant_t((IDispatch *)pMyConnect,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,"无法打开mytable表。","提醒",
MB_OK │ MB_ICONWARNING);
}
  Recordset工具的Open要领很是重要,它的第一个参数可所以一个SQL语句、一个表的名字或一个呼吁工具等等;第二个参数就是前面成立的毗连工具的指针。另外,用Connection和Command工具的Execute要领也能获得记录集,可是只读的。


  4、读取当前记录的数据

  我以为读取数据的最利便的要领如下:

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
//Retrieve column's value:
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("name"))->Value);
short cAge=(short)(m_pRecordset->Fields->GetItem
(_variant_t("age"))->Value);
//Do something what you want to do:
......
m_pRecordset->MoveNext();
}
}//try
catch (_com_error &e)
{
CString str=(char*)e.Description();
::MessageBox(NULL,str+"n又出短处了。","提醒",
MB_OK │ MB_ICONWARNING);
}
  本例中的name和age都是字段名,读取的字段值别离生涯在sName和cAge变量内。例中的Fields是Recordset工具的容器,GetItem要领返回的是Field工具,而Value则是Field工具的一个属性(即该字段的值)。通过此例,应把握哄骗工具属性的要领。譬喻,要得到Field 工具的Value属性的值可以直接用属性名Value来引用它(如上例),但也可以挪用Get要领,譬喻:

CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("name"))->GetValue());
  以后例还可以看到,判定是否达到记录集的末端,行使记录集的adoEOF属性,其值若为真即到了末了,反之则未到。判定是否达到记录集开头,则可用BOF属性。

  其它,读取数据尚有一个要领,就是界说一个绑定的类,然后通过绑定的变量获得字段值(详见后头的先容)。

  5、修改数据

  要领一:

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value=_bstr_t("赵薇");
......
m_pRecordset->Update();

m_pRecordset->MoveNext();
}
}//try
  改变了Value属性的值,即改变了字段的值。

  要领二:

m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->PutValue(_bstr_t("赵薇"));
  要领三:就是用界说绑定类的要领(详见后头的先容)。

  6、添加记录

  新记录添加乐成后,即自动成为当前记录。AddNew要领有两种情势,一个含有参数,而另一个则不带参数。

  要领一(不带参数):

// Add new record into this table:
try{
if(!m_pRecordset->Supports(adAddNew)) return;

m_pRecordset->AddNew();
m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value=_bstr_t("赵薇");
m_pRecordset->Fields->GetItem
(_variant_t("性别"))->Value=_bstr_t("女");
m_pRecordset->Fields->GetItem
(_variant_t("age"))->Value=_variant_t((short)20);
m_pRecordset->Fields->GetItem
(_variant_t("marry"))->Value=_bstr_t("未婚");
m_pRecordset->Update();
}//try
catch (_com_error &e)
{
::MessageBox(NULL, "又出短处了。","提醒",MB_OK │ MB_ICONWARNING);
}
  这种要领弄完了还要挪用Update()。

  要领二(带参数):

_variant_t varName[4],narValue[4];
varName[0] = L"姓名";
varName[1] = L"性别";
varName[2] = L"age";
varName[3] = L"marry";
narValue[0]=_bstr_t("赵薇");
narValue[1]=_bstr_t("女");
narValue[2]=_variant_t((short)20);
narValue[3]=_bstr_t("未婚");

const int nCrit = sizeof varName / sizeof varName[0];
// Create SafeArray Bounds and initialize the array
SAFEARRAYBOUND rgsaName[1],rgsaValue[1];
rgsaName[0].lLbound = 0;
rgsaName[0].cElements = nCrit;
SAFEARRAY *psaName = SafeArrayCreate( VT_VARIANT, 1, rgsaName );
rgsaValue[0].lLbound = 0;
rgsaValue[0].cElements = nCrit;
SAFEARRAY *psaValue = SafeArrayCreate( VT_VARIANT, 1, rgsaValue );
// Set the values for each element of the array
HRESULT hr1=S_OK.hr2=S_OK;
for( long i = 0 ; i < nCrit && SUCCEEDED( hr1 ) && SUCCEEDED( hr2 );i++)
{
hr1=SafeArrayPutElement(psaName, &i,&varName[i]);
hr2=SafeArrayPutElement(psaValue, &i,&narValue[i]); }

// Initialize and fill the SafeArray
VARIANT vsaName,vsaValue;
vsaName.vt = VT_VARIANT │ VT_ARRAY;
vsaValue.vt = VT_VARIANT │ VT_ARRAY;
V_ARRAY(&vsaName) = psaName;//&vsaName->parray=psaName;
//see definition in oleauto.h file.
V_ARRAY(&vsaValue) = psaValue;

// Add a new record:
m_pRecordset->AddNew(vsaName,vsaValue);
  这种要领不必要挪用Update,由于添加后,ADO会自动挪用它。此要领首要是行使SafeArray挺贫困。

  要领三:就是用界说绑定类的要领(详见后头的先容)。

 7、删除记录

  挪用Recordset的Delete要领就行了,删除的是当前记录。要相识Delete的其余用法请查阅参考文献。

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value);
if(::MessageBox(NULL,"姓名="+sName+"n删除她吗?",
"提醒",MB_YESNO │ MB_ICONWARNING)==IDYES)
{
m_pRecordset->Delete(adAffectCurrent);
m_pRecordset->Update();
}
m_pRecordset->MoveNext();
}
}//try
catch (_com_error &e)
{
::MessageBox(NULL,"又出短处了。","提醒",MB_OK │ MB_ICONWARNING);
}
  8、行使带参数的呼吁

  Command工具所代表的就是一个Provider可以或许领略的呼吁,如SQL语句等。行使Command工具的要害就是把暗示呼吁的语句配置到CommandText属性中,然后挪用Command工具的Execute要领就行了。一样平常环境下在呼吁中无需行使参数,但偶然行使参数,可以增进其机动性和服从。

  (1). 成立毗连、呼吁工具和记录集工具

  本例中暗示呼吁的语句就是一个SQL语句(SELECT语句)。SELECT语句中的问号?就代表参数,假如要多个参数,就多放几个问号,每个问号代表一个参数。

_ConnectionPtr Conn1;
_CommandPtr Cmd1;
ParametersPtr *Params1 = NULL; // Not an instance of a smart pointer.
_ParameterPtr Param1;
_RecordsetPtr Rs1;

try
{
// Create Connection Object (1.5 Version)
Conn1.CreateInstance( __uuidof( Connection ) );
Conn1->ConnectionString = bstrConnect;
Conn1->Open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );
// Create Command Object
Cmd1.CreateInstance( __uuidof( Command ) );
Cmd1->ActiveConnection = Conn1;
Cmd1->CommandText = _bstr_t("SELECT * FROM mytable WHERE age< ?");
}//try
  要留意呼吁工具必需与毗连工具关联起来才气起浸染,本例中将呼吁工具的ActiveConnection属性配置为毗连工具的指针,即为此目标:

Cmd1->ActiveConnection = Conn1;
  (2). 建设参数工具,并给参数赋值

// Create Parameter Object
Param1 = Cmd1->CreateParameter( _bstr_t(bstrEmpty),
adInteger,
adParamInput,
-1,
_variant_t( (long) 5) );
Param1->Value = _variant_t( (long) 5 );
Cmd1->Parameters->Append( Param1 );
  用呼吁工具的要领来建设一个参数工具,个中的长度参数(第三个)假如是牢靠长度的范例,就填-1,假如是字符串等可变长度的就填着实际长度。Parameters是呼吁工具的一个容器,它的Append要领就是把建设的参数工具追加到该容器里。Append进去的参数按先后次序与SQL语句中的问号从左至右逐一对应。

  (3). 执行呼吁打开记录集

// Open Recordset Object
Rs1 = Cmd1->Execute( &vtEmpty, &vtEmpty2, adCmdText );
  但要留意,用Command和Connection工具的Execute要领获得的Recordset是只读的。由于在打开Recordset之前,我们无法配置它的LockType属性(其默认值为只读)。而在打开之后配置LockType不起浸染。

  我发明用上述要领获得记录集Rs1后,不单Rs1中的记录无法修改,纵然直接用SQL语句修改统一表中任何记录都不可。

  要想能修改数据,照旧要用Recordset本身的Open要领才行,如:

try{
m_pRecordset->Open((IDispatch *) Cmd1, vtMissing,
adOpenStatic, adLockOptimistic, adCmdUnspecified);
}
catch (_com_error &e)
{
::MessageBox(NULL,"mytable表不存在。","提醒",MB_OK │ MB_ICONWARNING);
}
  Recordset工具的Open要领真是太好了,其第一个参数可所以SQL语句、表名字、呼吁工具指针等等。

  9、相应ADO的关照变乱

  关照变乱就是当某个特定变乱产生时,由Provider关照客户措施,换句话说,就是由Provider挪用客户措施中的一个特定的要领(即变乱的处理赏罚函数)。所觉得了相应一个变乱,最要害的就是要实现变乱的处理赏罚函数。

  (1). 从ConnectionEventsVt接口派生出一个类

  为了相应_Connection的关照变乱,应该从ConnectionEventsVt接口派生出一个类:

class CConnEvent : public ConnectionEventsVt
{
private:
ULONG m_cRef;
public:
CConnEvent() { m_cRef = 0; };
~CConnEvent() {};

STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);
STDMETHODIMP raw_BeginTransComplete(
LONG TransactionLevel,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);
......
};
   (2). 实现每一个变乱的处理赏罚函数(往往带raw_前缀的要领都把它实现了):

STDMETHODIMP CConnEvent::raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};
  有些要领固然你并不必要,但也必需实现它,只需简朴地返回一个S_OK即可。但假如要停止常常被挪用,还应在个中将adStatus参数配置为adStatusUnwantedEvent,则在本次挪用后,往后就不会被挪用了。
其它还必需实现QueryInterface, AddRef, 和Release三个要领:

STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)
{
*ppv = NULL;
if (riid == __uuidof(IUnknown) ││
riid == __uuidof(ConnectionEventsVt)) *ppv = this;
if (*ppv == NULL)
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
STDMETHODIMP_(ULONG) CConnEvent::Release()
{
if (0 != --m_cRef) return m_cRef;
delete this;
return 0;
}
  (3). 开始相应关照变乱

// Start using the Connection events
IConnectionPointContainer *pCPC = NULL;
IConnectionPoint *pCP = NULL;

hr = pConn.CreateInstance(__uuidof(Connection));
if (FAILED(hr)) return;

hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **)&pCPC);
if (FAILED(hr)) return;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return;

pConnEvent = new CConnEvent();
hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
if (FAILED(hr)) return rc;
hr = pCP->Advise(pUnk, &dwConnEvt);
pCP->Release();
if (FAILED(hr)) return;

pConn->Open("dsn=Pubs;", "sa", "", adConnectUnspecified);
  也就是说在毗连(Open)之前就做这些事。

  (4). 遏制相应关照变乱

pConn->Close();
// Stop using the Connection events
hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **) &pCPC);
if (FAILED(hr)) return;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;
hr = pCP->Unadvise( dwConnEvt );
pCP->Release();
if (FAILED(hr)) return;
  在毗连封锁之后做这件事。

(编辑:湖南网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读