OLEDB 简单数据查找定位和错误处理
在数据库查询中,我们主要使用的SQL语句,但是之前也说过,SQL语句需要经历解释执行的步骤,这样就会拖慢程序的运行速度,针对一些具体的简单查询,比如根据用户ID从用户表中查询用户具体信息,像这样的简单查询OLEDB提供了专门的查询接口。使用该接口可以很大程度上提升程序性能。
另外在之前的代码中,只是简单的通过HRESULT这个返回值来判断是否成功,针对错误没有具体的处理,但是OLEDB提供了自己的处理机制,这篇博文主要来介绍这两种情况下的处理方式
简单数据查询和定位
它的使用方法与之前的简单读取结果集类似,主要经历如下几部
- 绑定需要在查询中做条件的几列(绑定的方式与之前的相同)
- 分配一段内存,给定对应的条件值
- 循环调用IRowsetFind接口的FindNextRow方法,传入对应的结果集、条件、条件值的缓冲,接收函数返回的新的结果集指针
- 使用常规方法访问结果集
FindNextRow函数的定义如下:
HRESULT FindNextRow (
HCHAPTER hChapter,
HACCESSOR hAccessor, //绑定查询条件的访问器,用于OLEDB组件访问用户传进来的条件
void *pFindValue, //之前的内存缓冲
DBCOMPAREOP CompareOp, // 查询条件,主要有:DBCOMPAREOPS_EQ、DBCOMPAREOPS_NE、DBCOMPAREOPS_LT等等,具体的请参看MSDN
DBBKMARK cbBookmark,
const BYTE *pBookmark,
DBROWOFFSET lRowsOffset,
DBROWCOUNT cRows, //一次返回的行数
DBCOUNTITEM *pcRowsObtained, //真实返回的行
HROW **prghRows); //返回的行访问器的句柄数组
下面是一个具体的例子:
HRESULT hRes = pIRowset->QueryInterface(IID_IRowsetFind, (void**)&pIRowsetFind);
COM_SUCCESS(hRes, _T("查询接口IRowsetFind失败,错误码为:%08x\n"), hRes);
hRes = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
COM_SUCCESS(hRes, _T("查询接口IColumnInfo失败,错误码为:%08x\n"), hRes);
hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
COM_SUCCESS(hRes, _T("查询接口IAccessor失败,错误码为:%08x\n"), hRes);
hRes = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnsInfo, &lpColumnsName);
COM_SUCCESS(hRes, _T("查询列信息失败,错误码为:%08x\n"), hRes);
rgQueryBinding[0].bPrecision = rgColumnsInfo[1].bPrecision;
rgQueryBinding[0].bScale = rgColumnsInfo[1].bScale;
rgQueryBinding[0].cbMaxLen = sizeof(ULONG);
rgQueryBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgQueryBinding[0].dwPart = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
rgQueryBinding[0].eParamIO = DBPARAMIO_NOTPARAM;
rgQueryBinding[0].iOrdinal = 4; //绑定第4列,也就是表中的所属行政区编号列
rgQueryBinding[0].obStatus = 0;
rgQueryBinding[0].obLength = sizeof(DBSTATUS);
rgQueryBinding[0].obValue = sizeof(DBSTATUS) + sizeof(ULONG);
rgQueryBinding[0].wType = DBTYPE_I4;
hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgQueryBinding, 0, &hQueryAccessor, NULL);
COM_SUCCESS(hRes, _T("创建访问器失败,错误码为:%08x\n"), hRes);
pQueryBuff = COM_ALLOC(void, rgQueryBinding[0].obValue + sizeof(ULONG));
*(DBSTATUS*)((LPBYTE)pQueryBuff + rgQueryBinding[0].obValue) = DBSTATUS_S_OK;
*(ULONG*)((LPBYTE)pQueryBuff + rgQueryBinding[0].obLength) = sizeof(ULONG);
*(ULONG*)((LPBYTE)pQueryBuff + rgQueryBinding[0].obValue) = uId;
hRes = pIRowsetFind->FindNextRow(DB_NULL_HCHAPTER, hQueryAccessor, pQueryBuff, DBCOMPAREOPS_EQ, 0, NULL, 0, 10, &cRowsObtained, &rgShowRows);
COM_SUCCESS(hRes, _T("查询结果集失败,错误码为:%08x\n"), hRes);
bRet = ReadRows(cColumns, rgColumnsInfo, cRowsObtained, rgShowRows, pIRowset);
这段代码首先获取到对应列的列信息,然后根据这个列信息进行动态绑定,在这里我们绑定第4列,也就是之前行政区表的所属行政区编号列,接着针对这个绑定创建访问器,并分配缓冲存储对应的条件值,最后调用FindNextRow返回查询到的新的结果集,并调用对应的函数读取返回的结果集
上面的代码并不复杂,从FindNextRow的第4个参数的值来看,它只能支持简单的大于小于等于等等操作,像sql语句中的模糊查询,多表查询,联合查询等等它是不能胜任的,因此说它只是一个简单查询,它在某些简单场合下可以节省性能,但是对于复杂的业务逻辑中SQL语句仍然是不二的选择
错误处理
在windows中定义了丰富的错误处理代码和错误处理方式,几乎每种类型的程序都有自己的一套处理方式,比如Win32 API中的GetLastError,WinSock中的WSAGetLastError, 其实在OLEDB中有它自己的处理方式。
COM中可以使用GetErrorInfo函数得到一个错误的信息的接口,IErrorInfo,进一步可以根据该接口的对应函数可以得到具体的错误信息。
IErrorInfo接口
IErrorInfo 有时候自身包含一些出错信息,可以直接读取。IErrorInfo有时候只有一条错误信息,有时候是一个树形结构的错误信息通过调用QueryInterface函数查询错误对象的IErrorRecords接口来判定错误信息是否还有详细的子记录。如果有子记录。如果能得到IErrorRecords接口,就调用IErrorRecords::GetRecordCount获得错误信息记录个数,接着循环调用IErrorRecords::GetErrorInfo又取得子记录的IErrorInfo接口,并获取错误信息
若没有子错误记录,那么直接调用IErrorInfo::GetDescription得到错误描述信息,调用IErrorInfo::GetSource得到错误来源信息
以上所述IErrorInfo接口是COM定义的标准接口,IErrorRecords是OLEDB专门定义的错误信息记录接口。
IErrorRecords接口
其实IErrorRecords接口除了能获取子记录的IErrorInfo接口外还有一个重要的功能。调用接口的GetBasicErrorInfo方法可以得到一个指定索引错误记录的基本错误信息结构体ERRORINFO。
该结构的定义如下:
typedef struct tagERRORINFO {
HRESULT hrError;
DWORD dwMinor;
CLSID clsid;
IID iid;
DISPID dispid;
} ERRORINFO;
根据这个结构可以得到指定错误的具体信息,有点类似于FormatMessage格式化错误码,得到错误码对应的错误提示信息。
另外可以调用接口的GetCustomErrorObject给定一个错误码,得到一个具体的错误对象,一般在OLEDB中这个对象是ISQLErrorInfo接口
这两个函数的第一个参数是一个编号,这个编号一般是第几个IErrorRecords中错误信息的编号。
下面是一个具体的例子
LPOLESTR lpSQL = OLESTR("select * where aa26;select * from aa26 where, aac031;");
HRESULT hRes =pIOpenRowset->QueryInterface(IID_IDBCreateCommand, (void**)&pIDBCreateCommand);
COM_SUCCESS(hRes, _T("查询接口IDBCreateCommand失败,错误码为:%08x\n"), hRes);
LPOLESTR lpErrorInfo = (LPOLESTR)CoTaskMemAlloc(1024 * sizeof(WCHAR));
hRes = pIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown**)&pICommandText);
COM_SUCCESS(hRes, _T("创建接口ICommandText失败,错误码为:%08x\n"), hRes);
pICommandText->SetCommandText(DBGUID_DEFAULT, lpSQL);
hRes = pICommandText->Execute(NULL, IID_IRowset, NULL, NULL, (IUnknown**)&pIRowset);
if (FAILED(hRes))
{
GetErrorInfo(0, &pIErrorInfo);
HRESULT hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
if (SUCCEEDED(hr))
{
ULONG uRecordes = 0;
hr = pIErrorRecords->GetRecordCount(&uRecordes);
COM_SUCCESS(hr, _T("获取错误集个数失败,错误码为:%08x\n"), hr);
for (int i = 0; i < uRecordes; i++)
{
ReadErrorRecords(lpErrorInfo, 1024 * sizeof(WCHAR), hRes, i, pIErrorRecords, lpSQL);
COM_PRINTF(_T("%s"), lpErrorInfo);
}
}else
{
ReadErrorInfo(lpErrorInfo, 1024 * sizeof(WCHAR), hRes, pIErrorInfo);
COM_PRINTF(_T("%s"), lpErrorInfo);
}
}
在上述例子中,我们故意传入一个错误的SQL语句,让其出错,然后通过GetErrorInfo函数获取一个错误的IErrorInfo接口,尝试查询IErrorRecords,如果有那么在循环中遍历它的子集,并且得到每个子集的详细错误信息。否则直接调用函数ReadErrorInfo获取错误的具体信息
BOOL ReadErrorRecords(LPOLESTR &lpErrorInfo, DWORD dwSize, HRESULT hErrRes, ULONG uRecordIndex, IErrorRecords *pIErrorRecords, LPWSTR lpSql)
{
ERRORINFO ErrorInfo = {0};
static LCID lcid = GetUserDefaultLCID();
HRESULT hRes = pIErrorRecords->GetBasicErrorInfo(uRecordIndex, &ErrorInfo);
COM_CHECK_HR(hRes);
hRes = pIErrorRecords->GetErrorInfo(uRecordIndex, lcid, &pIErrorInfo);
COM_CHECK_HR(hRes);
hRes = pIErrorInfo->GetDescription(&pstrDescription);
COM_CHECK_HR(hRes);
hRes = pIErrorInfo->GetSource(&pstrSource);
COM_CHECK_HR(hRes);
if (ReadSQLError(&bstrSQLErrorInfo, pIErrorRecords, uRecordIndex))
{
StringCchPrintf(lpErrorInfo, dwSize, _T("\n(%s)\n错误信息: HRESULT=0x%08X\n描述: %s\nSQL错误信息: %s\n来源: %s"), lpSql, ErrorInfo.hrError, pstrDescription, bstrSQLErrorInfo, pstrSource);
}else
{
StringCchPrintf(lpErrorInfo, dwSize, _T("\n(%s)\n错误信息: HRESULT=0x%08X\n描述: %s\n来源: %s"), lpSql, ErrorInfo.hrError, pstrDescription, pstrSource);
}
}
该函数用于显示错误子集的信息,在函数中首先调用IErrorRecords接口的GetBasicErrorInfo函数传入子集的编号,获取子集的基本信息,然后再调用IErrorRecords接口的GetErrorInfo方法获取子集的IErrorInfo接口,接着调用IErrorInfo接口的相应函数获取错误的详细信息,在这个里面我们调用了另外一个自定义函数ReadSQLError,尝试获取在执行SQL语句时的错误,然后进行相关的输出。函数的部分代码如下:
BOOL ReadSQLError(BSTR *pbstrSQL, IErrorRecords *pIErrorRecords, DWORD dwRecordIndex)
{
HRESULT hRes = pIErrorRecords->GetCustomErrorObject(dwRecordIndex, IID_ISQLErrorInfo, (IUnknown**)&pISQLErrorInfo);
hRes = pISQLErrorInfo->GetSQLInfo(pbstrSQL, &lNativeErrror);
}
这个函数就简单的调用了IErrorRecords接口的GetCustomErrorObject方法传入子集的编号,获取到ISQLErrorInfo接口,最后调用ISQLErrorInfo接口的GetSQLInfo方法获取执行SQL语句时的错误。
至于函数ReadErrorInfo,它的代码十分简单,就只是ReadErrorRecords函数中关于IErrorInfo处理的部分代码而已,在这就不在说明
最后放上这两个例子的详细代码地址
OLEDB 简单数据查找定位和错误处理的更多相关文章
- SQL Server 2008 R2——根据数据查找表名和字段名 根据脏数据定位表和字段
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误
嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报 分类: 嵌入式(928) 一般察看函数运行时堆栈的 ...
- linux下利用backtrace追踪函数调用堆栈以及定位段错误
一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...
- 【Android Developers Training】 33. 接收来自其它应用的简单数据
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 用户态使用 glibc/backtrace 追踪函数调用堆栈定位段错误【转】
转自:https://blog.csdn.net/gatieme/article/details/84189280 版权声明:本文为博主原创文章 && 转载请著名出处 @ http:/ ...
- listctrl查找定位 使用测试过还很好用
35.listctrl查找定位 使用测试过还很好用 // 简单的查找函数// FindString(CListCtrl& , 查找内容 , 开始位置 , 到达底部时是否从头查找) int F ...
- C#在dataGridView中遍历,寻找相同的数据并定位
1. C#在dataGridView中遍历,寻找相同的数据并定位 [c-sharp] view plain copy int row = dataGridView1.Rows.Count;// ...
- Electron存储简单数据和用户首选项推荐用electron-store
electron-store1可以用来保存Electron应用程序或模块的简单数据持久性-保存和加载用户首选项,应用程序状态,缓存等. 1https://github.com/sindresorhus ...
- 在nginx服务器里面搭建好node.js本地服务器后,利用Node.js的FS模块,实现简单数据的写入和读取
先在server.js里面引入: var fs = require('fs'); 然后写入 // 往writeme.txt文件 写入一些内容 fs.writeFile('./writem ...
随机推荐
- 二分查找法C语言实现
[问题描述] 生成一个随机数组A[64] ,在数组中查找是否存在某个数num. [答案] #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> ...
- 解决tomcat启动 startup.bat的时候一闪而过(就是java环境变量的配置)
系统变量配置:(解决tomcat启动 startup.bat的时候一闪而过) JAVA_HOME C:\Program Files (x86)\Java\jdk1.7.0_25 =========== ...
- Eclipse中文件夹变成包的解决办法(python版)
问题展示如下: 如图,框中的三个文件夹都变成了包的样子. 解决方法如下: 1.在项目文件夹上右键,打开属性框 2.将PYTHONPATH中,Source Folders中的文件夹都删除.即可看到包已变 ...
- 构建docker镜像
一.通过docker commit命令构建镜像 docker commit 构建镜像可以想象为是将运行的镜像进行重命名另存一份.我们先创建一个容器,并在容器里做出修改,就像修改代码一样,最后再将修改提 ...
- jeesite模块解析,功能实现
做为十分优秀的开源框架,JeeSite拥有着很多实用性的东西. 默认根路径跳转 定义了无Controller的path<->view直接映射 <mvc:view-controller ...
- 008 Android activity实现多个界面的相互跳转(主要利用Intent)
1.activity介绍 一个activity就把他理解成一个页面 2.新建activity流程 如图所示在com.lucky.test06的目录下,右击new--->Activity---&g ...
- javascript JSON. 转换 注意事项
JSON.stringify() 会舍弃 方法..只有属性才会转换成 json 字符串,所以 用 JSON.stringify()=='{}' 来判断对象是否为空 是错误的!!!! 正确的做法 是 ...
- 洛谷 P3191 [HNOI2007]紧急疏散EVACUATE(网络最大流)
题解 二分答案+Dinic最大流 二分答案\(mid\) 把门拆成\(mid\)个时间点的门 相邻时间的门连一条\(inf\)的边 预处理出每个门到每个人的最短时间 为\(dis[k][i][j]\) ...
- C++ GUI Qt4编程(11)-5.1hexSpinbox
1. hexspinbox.cpp /* * The spin box supports integer values but can be extended to use different str ...
- CentOS-pam认证机制简介
前言 linux下PAM模块全称是Pluggable Authentication Module for linux(可插入式授权管理模块),该由Sun公司提供,在Linux中,PAM是可动态配置的, ...