仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1136|回复: 8
打印 上一主题 下一主题

[学习教程] MSSQL网页编程之Visual C++ ADO数据库编程进门(上)

[复制链接]
变相怪杰 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-16 22:39:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
mysql的prepare其实是本地PHP客户端模拟的,并没有根据你mysql的设置做字符集的调整。应该交与mysqlserver端做prepare,同时得调用mysql_set_character_set去操作,server才会按照字符集去做转义。ado|c++|visual|编程|数据|数据库  ADO是今朝在Windows情况中对照盛行的客户端数据库编程手艺。ADO是创建在OLEDB底层手艺之上的初级编程接口,因此它兼具有壮大的数据处置功效(处置各类分歧范例的数据源、散布式的数据处置等等)和极为复杂、易用的编程接口,因此失掉了普遍的使用。并且按微软公司的企图,OLEDB和ADO将慢慢代替ODBC和DAO。如今先容ADO各类使用的文章和书本有良多,本文侧重站在初学者的角度,扼要切磋一下在VC++中利用ADO编程时的一些成绩。我们但愿浏览本文之前,您对ADO手艺的基础道理有一些懂得。

  1、在VC++中利用ADO编程

  ADO实践上就是由一组Automation工具组成的组件,因而能够象利用别的任何Automation工具一样利用ADO。ADO中最主要的工具有三个:Connection、Command和Recordset,它们分离暗示毗连工具、命令工具和纪录集工具。假如您熟习利用MFC中的ODBC类(CDatabase、CRecordset)编程,那末进修ADO编程就非常简单了。

  利用ADO编程时能够接纳以下三种办法之一:

  1、利用预处置指令#import

#import"C:ProgramFilesCommonFilesystemADOmsado15.dll"
no_namespacerename("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

  如利用以下代码:

CLSIDclsid;
HRESULThr=::CLSIDFromProgID(L"ADODB.Connection",&clsid);
if(FAILED(hr))
{...}
::CoCreateInstance(clsid,NULL,CLSCTX_SERVER,IID_IDispatch,(void**)
&pDispatch);
if(FAILED(hr))
{...}
  以上三种办法,第一和第二品种似,大概第一种好用一些,第三种编程大概最贫苦。但大概第三种办法也是效力最高的,程序的尺寸也最小,而且对ADO的把持才能也最强。

  据微软材料先容,第一种办法不撑持办法挪用中的默许参数,固然第二种办法也是如许,但第三种就不是如许了。接纳第三种办法的程度也最高。当你必要绕过ADO而间接挪用OLEDB底层的办法时,就必定要利用第三种办法了。

  ADO编程的关头,就是纯熟地使用ADO供应的各类工具(object)、办法(method)、属性(property)和容器(collection)。别的,假如是在MSSQL或Oracle等年夜型数据库上编程,还要能纯熟利用SQL言语。


  2、利用#import办法的编程步骤

  这里倡议您利用#import的办法,由于它易学、易用,代码也对照简便。

  1、增加#import指令

  翻开stdafx.h文件,将以下内容增加到一切的include指令以后:

#include<icrsint.h>//IncludesupportforVC++Extensions
#import"C:ProgramFilesCommonFilesystemADOmsado15.dll"
no_namespacerename("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)、毗连到MSSQLServer

  注重毗连字符串的格局,供应准确的毗连字符串是乐成毗连到数据库服务器的第一步,有干系接字符串的具体信息拜见微软MSDNLibrary光盘。

  本例毗连字符串中的server_name,database_name,user_name和password在编程时都应当交换成实践的内容。

_ConnectionPtrpMyConnect=NULL;
HRESULThr=pMyConnect.CreateInstance(__uuidof(Connection)));
if(FAILED(hr))return;

_bstr_tstrConnect="Provider=SQLOLEDB;Server=server_name;"
"Database=database_name;uid=user_name;pwd=password;";
//connectingtothedatabaseservernow:
try{pMyConnect->Open(strConnect,"","",NULL);}
catch(_com_error&e)
{
::MessageBox(NULL,e.Description(),"告诫",MB_OK│MB_ICONWARNING);
}
  注重Connection工具的Open办法中的毗连字符串参数必需是BSTR或_bstr_t范例。别的,本例是间接经由过程OLEDBProvider创建毗连,以是无需创建数据源。

  (2)、经由过程ODBCDriver毗连到DatabaseServer毗连字符串格局与间接用ODBC编程时的差未几:

_bstr_tstrConnect="DSN=datasource_name;Database=database_name;uid=user_name;pwd=password;";
  此时与ODBC编程一样,必需先创建数据源。

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

  界说_RecordsetPtr型变量,然后经由过程它挪用Recordset工具的Open办法,便可翻开一个数据集。以是Recordset工具与MFC中的CRecordset类相似,它也有以后纪录、以后纪录指针的观点。如:

_RecordsetPtrm_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)
{
//Retrievecolumnsvalue:
CStringsName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("name"))->Value);
shortcAge=(short)(m_pRecordset->Fields->GetItem
(_variant_t("age"))->Value);
//Dosomethingwhatyouwanttodo:
......
m_pRecordset->MoveNext();
}
}//try
catch(_com_error&e)
{
CStringstr=(char*)e.Description();
::MessageBox(NULL,str+"
又出偏差了。","提醒",
MB_OK│MB_ICONWARNING);
}
  本例中的name和age都是字段名,读取的字段值分离保留在sName和cAge变量内。例中的Fields是Recordset工具的容器,GetItem办法前往的是Field工具,而Value则是Field工具的一个属性(即该字段的值)。经由过程此例,应把握利用工具属性的办法。比方,要取得Field工具的Value属性的值能够间接用属性名Value来援用它(如上例),但也能够挪用Get办法,比方:

CStringsName=(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办法有两种情势,一个含有参数,而另外一个则不带参数。

  办法一(不带参数):

//Addnewrecordintothistable:
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_tvarName[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("未婚");

constintnCrit=sizeofvarName/sizeofvarName[0];
//CreateSafeArrayBoundsandinitializethearray
SAFEARRAYBOUNDrgsaName[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);
//Setthevaluesforeachelementofthearray
HRESULThr1=S_OK.hr2=S_OK;
for(longi=0;i<nCrit&&SUCCEEDED(hr1)&&SUCCEEDED(hr2);i++)
{
hr1=SafeArrayPutElement(psaName,&i,&varName[i]);
hr2=SafeArrayPutElement(psaValue,&i,&narValue[i]);}

//InitializeandfilltheSafeArray
VARIANTvsaName,vsaValue;
vsaName.vt=VT_VARIANT│VT_ARRAY;
vsaValue.vt=VT_VARIANT│VT_ARRAY;
V_ARRAY(&vsaName)=psaName;//&vsaName->parray=psaName;
//seedefinitioninoleauto.hfile.
V_ARRAY(&vsaValue)=psaValue;

//Addanewrecord:
m_pRecordset->AddNew(vsaName,vsaValue);
  这类办法不必要挪用Update,由于增加后,ADO会主动挪用它。此办法次要是利用SafeArray挺贫苦。

  办法三:就是用界说绑定类的办法(详见前面的先容)。

 7、删除纪录

  挪用Recordset的Delete办法就好了,删除的是以后纪录。要懂得Delete的别的用法请查阅参考文献。

try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
CStringsName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t("姓名"))->Value);
if(::MessageBox(NULL,"姓名="+sName+"
删除她吗?",
"提醒",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语句中的问号?就代表参数,假如要多个参数,就多放几个问号,每一个问号代表一个参数。

_ConnectionPtrConn1;
_CommandPtrCmd1;
ParametersPtr*Params1=NULL;//Notaninstanceofasmartpointer.
_ParameterPtrParam1;
_RecordsetPtrRs1;

try
{
//CreateConnectionObject(1.5Version)
Conn1.CreateInstance(__uuidof(Connection));
Conn1->ConnectionString=bstrConnect;
Conn1->Open(bstrEmpty,bstrEmpty,bstrEmpty,-1);
//CreateCommandObject
Cmd1.CreateInstance(__uuidof(Command));
Cmd1->ActiveConnection=Conn1;
Cmd1->CommandText=_bstr_t("SELECT*FROMmytableWHEREage<?");
}//try
  要注重命令工具必需与毗连工具联系关系起来才干起感化,本例中将命令工具的ActiveConnection属性设置为毗连工具的指针,即为此目标:

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

//CreateParameterObject
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).实行命令翻开纪录集

//OpenRecordsetObject
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接口派生出一个类:

classCConnEvent:publicConnectionEventsVt
{
private:
ULONGm_cRef;
public:
CConnEvent(){m_cRef=0;};
~CConnEvent(){};

STDMETHODIMPQueryInterface(REFIIDriid,void**ppv);
STDMETHODIMP_(ULONG)AddRef(void);
STDMETHODIMP_(ULONG)Release(void);
STDMETHODIMPraw_InfoMessage(
structError*pError,
EventStatusEnum*adStatus,
struct_Connection*pConnection);
STDMETHODIMPraw_BeginTransComplete(
LONGTransactionLevel,
structError*pError,
EventStatusEnum*adStatus,
struct_Connection*pConnection);
......
};
   (2).完成每个事务的处置函数(但凡带raw_前缀的办法都把它完成了):

STDMETHODIMPCConnEvent::raw_InfoMessage(
structError*pError,
EventStatusEnum*adStatus,
struct_Connection*pConnection)
{
*adStatus=adStatusUnwantedEvent;
returnS_OK;
};
  有些办法固然你其实不必要,但也必需完成它,只需复杂地前往一个S_OK便可。但假如要制止常常被挪用,还应在个中将adStatus参数设置为adStatusUnwantedEvent,则在本次挪用后,今后就不会被挪用了。
别的还必需完成QueryInterface,AddRef,和Release三个办法:

STDMETHODIMPCConnEvent::QueryInterface(REFIIDriid,void**ppv)
{
*ppv=NULL;
if(riid==__uuidof(IUnknown)││
riid==__uuidof(ConnectionEventsVt))*ppv=this;
if(*ppv==NULL)
returnResultFromScode(E_NOINTERFACE);
AddRef();
returnNOERROR;
}
STDMETHODIMP_(ULONG)CConnEvent::AddRef(){return++m_cRef;};
STDMETHODIMP_(ULONG)CConnEvent::Release()
{
if(0!=--m_cRef)returnm_cRef;
deletethis;
return0;
}
  (3).入手下手呼应关照事务

//StartusingtheConnectionevents
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=newCConnEvent();
hr=pConnEvent->QueryInterface(__uuidof(IUnknown),(void**)&pUnk);
if(FAILED(hr))returnrc;
hr=pCP->Advise(pUnk,&dwConnEvt);
pCP->Release();
if(FAILED(hr))return;

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

  (4).中断呼应关照事务

pConn->Close();
//StopusingtheConnectionevents
hr=pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void**)&pCPC);
if(FAILED(hr))return;
hr=pCPC->FindConnectionPoint(__uuidof(ConnectionEvents),&pCP);
pCPC->Release();
if(FAILED(hr))returnrc;
hr=pCP->Unadvise(dwConnEvt);
pCP->Release();
if(FAILED(hr))return;
  在毗连封闭以后做这件事。每个Rows_log_event中包含event_type,可选值为WRITE_ROWS_EVENT、UPDATE_ROWS_EVENT、DELETE_ROWS_EVENT。从宏名字就能看出用途。
莫相离 该用户已被删除
沙发
发表于 2015-1-19 20:52:56 | 只看该作者
原理很简单,对要求长时间计算某一时间点的报表生成和防用户操作错误很有帮助。但是比起Oracle10g的闪回技术还是细粒度不够。可惜!
admin 该用户已被删除
板凳
发表于 2015-1-27 17:06:15 | 只看该作者
学习SQL语言的话如果要学会去做网站就不是很难!但是要做数据库管理的话就有难度了!
分手快乐 该用户已被删除
地板
发表于 2015-2-5 09:16:03 | 只看该作者
对于微软系列的东西除了一遍遍尝试还真没有太好的办法
老尸 该用户已被删除
5#
发表于 2015-2-11 08:24:55 | 只看该作者
对于微软系列的东西除了一遍遍尝试还真没有太好的办法
变相怪杰 该用户已被删除
6#
 楼主| 发表于 2015-3-2 03:36:36 | 只看该作者
SQLServer的异构移植功能个人感觉最好了。(如果对比过SQLServer的链接服务器和Oracle的透明网关的朋友会发现SQLServer的sp_addlinkedserver(openquery)异构数据库系列比Oracle真是强太多了。)
深爱那片海 该用户已被删除
7#
发表于 2015-3-11 02:08:02 | 只看该作者
个人感觉没有case直观。而且默认的第三字段(还可能更多)作为groupby字段很容易造成新手的错误。
小魔女 该用户已被删除
8#
发表于 2015-3-17 18:47:59 | 只看该作者
但换公司用MSSQL2K感觉自己好像根本就不了解MSSQL。什么DTS触发器以前根本没用过。
简单生活 该用户已被删除
9#
发表于 2015-3-24 19:30:38 | 只看该作者
以前的DTS轻盈简单。但是现在的SSIS虽然功能强大了很多,但是总是让人感觉太麻烦。看看论坛中询问SSIS的贴子就知道。做的功能太强大了,往往会有很多用户不会用了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-3 12:00

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表