文章目录
问题提出
之前使用的是的ODBC连接sql server数据库,有需要可参考我的这篇文章:
https://blog.youkuaiyun.com/lishenluo/article/details/107045918。
相比ODBC网上说ADO是要好用一些,然后ODBC需要单独配置ODBC数据源,有点傻,不利于运维。所以改写程序,使用ADO实现。
实现
找对msado.dll库
本电脑应该就有,搜索下,然后复制出来放到工程的固定位置中。
代码实现
写代码实现数据库连接,选择操作,执行别的sql语句等通用实现。
AdoSql.h
#pragma once
//收下引入命令会导致4146警告,通过些命令去除
//#pragma warning(disable:4146)
//引号里面的路径,可绝对的,可相对的
//重命名EOF与BOF以免与其他命名空间冲突
#ifdef _DEBUG
#import "../../_runtime/x64.Debug/msado15.dll" named_guids rename("EOF", "adoEOF"), rename("BOF", "adoBOF")
#else
#import "../../_runtime/x64.Release/msado15.dll" named_guids rename("EOF", "adoEOF"), rename("BOF", "adoBOF")
#endif
using namespace ADODB;
namespace adosql
{
class AdoCenter
{
public:
AdoCenter();
~AdoCenter();
//select数据
int selectData(const string& sql, vector<string>& results);
//执行delete和insert等操作
int executeSql(const string& sql);
private:
bool connect();
private:
_ConnectionPtr m_pConn;
_RecordsetPtr m_pRecordset;
string m_connStr;
};
}
.cpp
#include “AdoSql.h”
using namespace adosql;
AdoCenter::AdoCenter()
{
m_connStr = “Provider=SQLOLEDB.1;Password=xxxxx;Persist Security Info=True;User ID=XXXXXX;Initial Catalog=YourDataBaseName;Data Source=127.0.0.1,1433”; //连接串,包括数据库名称,用户名,密码,数据库ip和端口
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr)) //返回值可判断初始化COM是否成功,请用SUCCEEDED来判断
{
connect();
}
else
{
std::cout<<"COM CoInitialize HRESULT error"<<std::endl;
}
}
AdoCenter::~AdoCenter()
{
if (NULL != m_pRecordset)
{
m_pRecordset->Close();
}
if (NULL != m_pConn)
{
m_pConn->Close();
}
CoUninitialize();
}
bool AdoCenter::connect()
{
if (NULL == m_pConn || adStateClosed == m_pConn->State)
{
try
{ //Connecting
if (!FAILED(m_pConn.CreateInstance(_uuidof(Connection)))) //设置连接超时时间
{
m_pConn->CommandTimeout = 30; //设置连接超时值,单位为秒
if (!FAILED(m_pConn->Open((_bstr_t)(m_connStr.c_str()), "", "", adModeUnknown)))
{
return true;
}
}
}
catch (_com_error e)
{
std::cout<<"connect to db error:"<<(char*)(e.Description())<<std::endl;
}
}
return false;
}
//改方法后文有改进版本
int AdoCenter::selectData(const string& sql, vector<string>& results)
{
int ret = -1;
if (NULL == m_pConn || adStateClosed == m_pConn->State)
{
connect();
}
try
{
//这里每次调用都创建实例比较慢
if (!FAILED(m_pRecordset.CreateInstance(__uuidof(Recordset))))
{
m_pRecordset->Open((_bstr_t)sql.c_str(), _variant_t((IDispatch*)m_pConn, true),
adOpenKeyset, adLockOptimistic, adCmdText);
long line = 0;
while (NULL != m_pRecordset && !m_pRecordset->adoEOF && adStateClosed != m_pRecordset->State)
{
long count = m_pRecordset->Fields->Count;
++line;
stringstream ss;
ss << line;
for (long i = 0; i < count; ++i)
{
//按名称获取数据
//_variant_t Column("lastUpdDt");
//_variant_t RusultGet = m_pRecordset->Fields->GetItem(Column)->Value;
//按列序号,从0开始
_variant_t rusultGet = m_pRecordset->Fields->GetItem(long(i))->Value;
char midData[MAXCHAR] = { 0 };
rusultGet.ChangeType(VT_BSTR);//统一转成字符串,否则下面有可能出错
WideCharToMultiByte(CP_ACP, 0, rusultGet.bstrVal, -1, midData, MAXCHAR, NULL, NULL);
ss << "," << midData;
}
results.emplace_back(ss.str());
m_pRecordset->MoveNext();
}
m_pRecordset->Close();
m_pRecordset = NULL;
ret = 0;
}
else
{
std::cout<<"create m_pRecordset failed"<<std::endl;
}
}
catch (_com_error e)
{
std::cout<<"Execute sql error:" << (char*)(e.Description()) << " sql=" << sql<<std::endl;
}
return ret;
}
int AdoCenter::executeSql(const string& sql)
{
int ret = -1;
if (sql.size() > 0)
{
if (NULL == m_pConn || adStateClosed == m_pConn->State)
{
connect();
}
try
{
_variant_t RefreshNum;
m_pConn->Execute(_bstr_t(sql.c_str()), &RefreshNum, adCmdText);
//ret = RefreshNum.lVal; //更新行数
ret = 0;
}
catch (_com_error e)
{
std::cout<<"Execute sql error:"<< (char*)(e.Description())<<", sql:" << sql<<std::endl;
}
}
return ret;
}
结论
网上说ADO比ODBC快,我实际应用好似是慢一些的,没有实际统计过。
上面的例子是我实际项目中的使用。多了一些配置文件读取,日志输出,项目导出UTILS_EXPORT设置,把这些删去就能编译过。简单好用。
参考
1.ADO编程详解(C++),连接串connectStr怎么获取参考这里。
2.ADO编程详解(C++),是1的转载和一些修改,排版好一些。不过例子有问题。 _RecordsetPtr 和 _RecordPtr 混用了。且实现得很繁琐,弄那么多方法做啥子。
[DBNETLIB][ConnectionRead (recv()).]一般性网络错误。请检查网络文档,问题处理
需要长时间运行程序,存数据库,如一两天,但是发现运行一段时间后,中间存库的时候报上面的异常。网络错误。查找了一通,都不行。直接用程序解决,重建建立数据库连接。
修改如下:
int AdoCenter::executeSql(const string& sql)
{
int ret = -1;
if (sql.size() > 0)
{
if (NULL == m_pConn || adStateClosed == m_pConn->State)
{
reConnect();
}
try
{
_variant_t RefreshNum;
m_pConn->Execute(_bstr_t(sql.c_str()), &RefreshNum, adCmdText);
//ret = RefreshNum.lVal; //更新行数
ret = 0;
}
catch (_com_error e)
{
std::cout<<"Execute sql error,retry,"<< (char*)(e.Description())<<" sql:" << sql<<std::endl;
//偶发连接错误处理
//报:[DBNETLIB][ConnectionRead (recv()).]一般性网络错误。请检查网络文档
//处理 lsl-20200828
//网上说的是Data Source=10.237.103.28,1433,后面要加上端口号
string errorInfo(e.Description());
if (errorInfo.find("DBNETLIB") != string::npos)
{
if (reConnect())
{
ret = executeSql(sql);//再次执行
}
if (ret != 0)
{
std::cout<<"retry Execute error,error:" << (char*)(e.Description()) << " sql:" << sql<<std::endl;
}
}
}
}
return ret;
}
重连方法实现:
bool AdoCenter::reConnect()
{
long reConnectTimes = 0;
while (true)
{
++reConnectTimes;
std::cout<<"re connect times:" << reConnectTimes<<std::endl;
Sleep(2000);
m_pConn = NULL;
if (connect())//直到成功
{
return true;
}
}
return false;
}
这里我用的是不成功不退出循环,你可以根据需要修改,如连接10次不成功就算了。
20200908 ado查询效率问题,改进
原来int AdoCenter::selectData(const string& sql, vector& results)方法实现有问题,每次都创建_RecordsetPtr实例,然后open,close。直接改用Execute函数,返回_RecordsetPtr就行。
代码如下:
int AdoCenter::selectData(const string& sql, vector<string>& results)
{
int ret = -1;
if (sql.size() > 0)
{
if (NULL == m_pConn || adStateClosed == m_pConn->State)
{
reConnect();
}
try
{
_variant_t RefreshNum;
//改进,直接用Execute,返回_RecordsetPtr就很快,很棒!
_RecordsetPtr pRecordset = m_pConn->Execute(_bstr_t(sql.c_str()), &RefreshNum, adCmdText);
long line = 0;
while (NULL != pRecordset && !pRecordset->adoEOF && adStateClosed != pRecordset->State)
{
long count = pRecordset->Fields->Count;
++line;
stringstream ss;
ss << line;
for (long i = 0; i < count; ++i)
{
//按列序号,从0开始
_variant_t rusultGet = pRecordset->Fields->GetItem(long(i))->Value;
char midData[MAXCHAR] = { 0 };
rusultGet.ChangeType(VT_BSTR);//统一转成字符串,否则下面有可能出错
WideCharToMultiByte(CP_ACP, 0, rusultGet.bstrVal, -1, midData, MAXCHAR, NULL, NULL);
ss << "," << midData;
}
results.emplace_back(ss.str());
pRecordset->MoveNext();
}
pRecordset = NULL;
ret = 0;
}
catch (_com_error e)
{
std::cout<<"Execute sql error,retry," << (char*)(e.Description()) << " sql:" << sql<<std::endl;
//偶发连接错误处理
//报:[DBNETLIB][ConnectionRead (recv()).]一般性网络错误。请检查网络文档
//处理 lsl-20200828
//网上说的是Data Source=10.237.103.28,1433,后面要加上端口号
string errorInfo(e.Description());
if (errorInfo.find("DBNETLIB") != string::npos)
{
if (reConnect())
{
ret = selectData(sql, results);//再次执行
}
if (ret != 0)
{
std::cout<<"retry Execute error,error:" << (char*)(e.Description()) << " sql:" << sql<<std::endl;
}
}
}
}
return ret;
}
修改前后对比,快了差不多四倍。
全市场,1.5W次查询。
修改前:
[2020-09-08 11:09:50,052.022] [108716] [INFO] [eqdata::HqData::fillData] calc micro end! total time cost:43.79(minutes), this time cost: 1.34(seconds)
修改后:
[2020-09-08 15:32:55,124.280] [123444] [INFO] [eqdata::HqData::fillData] calc micro end! total time cost:12.45(minutes), this time cost: 0.38(seconds)