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; 在毗连封锁之后做这件事。 (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐
热点阅读