项目背景:
该项目为vc++的控制台程序,数据库用的是mysql,数据库操作选来选去最终还是决定用ado,文章结束附带了一个别人封装的ado类,自己进行了一些优化。
遇到的问题:
1、通过ado调用存储过程;
2、数据库断线重连;
解决办法:
1、存储过程调用示例:
void ExecuteManageDBTables()
{
try
{
HRESULT re= m_adoHelper.m_pCommand.CreateInstance(__uuidof(Command));
inParam1.CreateInstance(__uuidof(Parameter));
//outParam1.CreateInstance(__uuidof(Parameter));
m_adoHelper.m_pCommand->CommandType = adCmdStoredProc;
m_adoHelper.m_pCommand->CommandText = _bstr_t("R_AutoCreateTables");//存储过程的名称
m_adoHelper.m_pCommand->ActiveConnection = m_adoHelper.m_pConnection;//需要使用的ADO连接
//输入参数
_variant_t vvar1;
SYSTEMTIME sys;
GetLocalTime(&sys);
char tmNow[30];
sprintf(tmNow, "%04d-%02d-%02d %02d:%02d:%02d", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
vvar1 = _variant_t(_bstr_t(tmNow));
inParam1 = m_adoHelper.m_pCommand->CreateParameter(_bstr_t("@time"), adVarChar, adParamInput, 32, _bstr_t(vvar1));
m_adoHelper.m_pCommand->Parameters->Append(inParam1);
//输出参数
//outParam1 = m_adoHelper.m_pCommand->CreateParameter("@P4", adInteger, adParamOutput, sizeof(int));//int型参数
//执行
m_adoHelper.m_pCommand->Execute(NULL, NULL, adCmdStoredProc);
m_adoHelper.m_pCommand.Release();//没有它会造成内存泄露
}
catch (_com_error& error)
{
LeaveCriticalSection(&m_csDB1);
wstring wDisp(error.Description());
string sDisp;
m_adoHelper.WideStringToString(wDisp, sDisp);
string errorMsg;
errorMsg = "ExecuteManageDBTables失败," + sDisp;
std::cout << (char*)errorMsg.c_str() << std::endl;
}
}
2、数据库断线重连
需要实例化两个ADO对象,m_adoHelper和m_adoHelper2。其中m_adoHelper用来进行数据的增删改查以及存储过程的调用,m_adoHelper2则用来监测数据库的通断状态,通断的监测我是放在一个线程里的,也就是通过m_adoHelper2定期执行一小段sql语句:m_bRet = m_adoHelper2.ExcuteSQL("SELECT NOW();"); 如果m_bRet 为false则说明数据库链接已经中断。主线程定时器定期监测m_bRet值,如果为false则进行数据库的重连操作DBConnect()。废话不多说,直接上代码:
m_bDBConnected为全局变量,记录数据库的实时链接状态
//主线程定时器,3秒执行一次
void PASCAL TimeProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
if (!m_bDBConnected)
{
DBDisConnect();
if (DBConnect())
{
m_bDBConnected = true;
}
}
}
//断开数据库
void DBDisConnect()
{
char info[255];
EnterCriticalSection(&m_csDB1);
m_adoHelper.DisconnectDatabase();
m_adoHelper.m_pConnection.Release();
LeaveCriticalSection(&m_csDB1);
EnterCriticalSection(&m_csDB2);
m_adoHelper2.DisconnectDatabase();
m_adoHelper2.m_pConnection.Release();
LeaveCriticalSection(&m_csDB2);
}
//链接数据库
bool DBConnect()
{
bool bRet = true;
char info[255];
try
{
std::string server="127.0.0.1";
std::string uid="root";
std::string pwd="123456";
std::string driver = "MySQL ODBC 8.0 ANSI Driver";
std::string pwd="dbname";
EnterCriticalSection(&m_csDB1);
m_adoHelper.m_pConnection.CreateInstance(__uuidof(Connection));
bool bRet1 = m_adoHelper.ConnectDatabase(AdoHelper::DatabaseMySQL,driver, server, uid, pwd, db);
LeaveCriticalSection(&m_csDB1);
EnterCriticalSection(&m_csDB2);
m_adoHelper2.m_pConnection.CreateInstance(__uuidof(Connection));
bool bRet2 = m_adoHelper2.ConnectDatabase(AdoHelper::DatabaseMySQL,driver, server, uid, pwd, db);
LeaveCriticalSection(&m_csDB2);
}
catch (BwAdoException& e) {
bRet = false;
sprintf(info, "%s,请核查数据库服务是否启动以及Config.ini文件中相关配置参数是否正确!", e.GetWhat().c_str());
cout << info << endl;
}
return bRet;
}
//子线程数据库状态监测,每3秒核查一次数据库链接状态,根据核查结果改变m_bDBConnected 值
UINT WINAPI CDBConnectThread::WorkThread(LPVOID lpParama)
{
CDBConnectThread *p = (CDBConnectThread *)lpParama;
if (!p)
return 0;
SYSTEMTIME timeNow, now;
COleDateTime startTime;
COleDateTime endTime;
GetLocalTime(&now);
static SYSTEMTIME timeStampDBConnect = now;
while (true) {
if (p->bThreadExit) break;
GetLocalTime(&timeNow);
startTime.SetDateTime(timeStampDBConnect.wYear, timeStampDBConnect.wMonth, timeStampDBConnect.wDay, timeStampDBConnect.wHour, timeStampDBConnect.wMinute, timeStampDBConnect.wSecond);
endTime.SetDateTime(timeNow.wYear, timeNow.wMonth, timeNow.wDay, timeNow.wHour, timeNow.wMinute, timeNow.wSecond);
COleDateTimeSpan timeSpan = endTime - startTime;
if (timeSpan.GetTotalSeconds() >= 3)
{
EnterCriticalSection(&m_csDB2);
bool bRet = false;
bool bRet2 = false;
bRet = m_adoHelper2.ExcuteSQL("SELECT NOW();");
LeaveCriticalSection(&m_csDB2);
if (!bRet)
{
EnterCriticalSection(&m_csDB1);
m_adoHelper.DisconnectDatabase();
LeaveCriticalSection(&m_csDB1);
EnterCriticalSection(&m_csDB2);
m_adoHelper2.DisconnectDatabase();
LeaveCriticalSection(&m_csDB2);
m_bDBConnected = false;
}
else
{
m_bDBConnected = true;
}
timeStampDBConnect = timeNow;
}
::Sleep(1);
}
return 0;
}
附件ado操作类:
AdoHelper.h
#ifndef _BW_ADO_H_
#define _BW_ADO_H_
#if _MSC_VER >= 1400
#pragma once
#endif // _MSC_VER >= 1400
#if __cplusplus < 199711L
#error 本类只能由支持 C++ 11 以上编译器编译
#endif // __cplusplus <
//
// 头文件
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <typeinfo>
#include <initializer_list>
using namespace std;
//
// ADO 库
#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" rename("EOF","adoEOF"),rename("BOF","adoBOF")
using namespace ADODB;
//
// 类声明
class AdoHelper;
class BwAdoException;
//
// 类定义
class AdoHelper final {
// 构造函数
public:
AdoHelper();
~AdoHelper();
// 链接与断开连接操作
public:
// 链接到数据库
bool ConnectDatabase(const int DatabaseType , std::string driver, std::string server, std::string uid, std::string pwd,std::string db);
// 断开连接
void DisconnectDatabase();
// 执行 SQL 语句
bool ExcuteSQL( const string& Command);
// 宽字符到窄字符
static size_t WideStringToString( wstring& Source, string& Dest );
// 窄字符到宽字符
size_t StringToWideString( string& Source, wstring& Dest );
// I/O 操作
public:
// 设置二进制大信息
bool SetBigTableValue(
const string& ItemName,
char* Value,
const size_t ValueSize );
// 光标操作
public:
// 移动光标
bool MovePtr(
const int Operation );
bool MovePtr(
const int Operation,
const int Step );
public:
// 数据库类型
enum DatabaseType {
DatabaseSQLServer, // SQLServer
DatabaseAccess, // Access
DatabaseMySQL, // MySQL
DatabaseOwn // 自定义
};
// 光标操作
enum PtrOperation {
PtrPrevious, // 移动到前一条记录
PtrNext, // 移动到后一条记录
PtrLast, // 移动到最后一条记录
PtrFirst, // 移动到第一条记录
};
private:
// 设置数据操作
template<typename T>
void SetValue(
const string& ItemName,
const T Value ) {
m_pRecordSet->PutCollect( ItemName.c_str(), _variant_t( Value ) );
}
// 私有成员
public:
_ConnectionPtr m_pConnection{ nullptr }; // 连接对象
_RecordsetPtr m_pRecordSet{ nullptr }; // 记录集
_CommandPtr m_pCommand{ nullptr }; // 命令对象
string m_sCurrentCmd; // 当前命令
};
//
class BwAdoException final {
// 构造与析构函数
public:
BwAdoException( const int Type ) {
// 错误描述
pair<int, string> mapping_buffer[] = {
{ ExpArgNotMatch, "函数参数个数和要求不匹配" },
{ ExpAccessViolation, "数组访问越界" },
{ ExpArgNotDefined, "给定参数未定义" },
{ ExpArgNotValid, "给定参数无效" }
};
m_mExpStringMap.insert( begin( mapping_buffer ), end( mapping_buffer ) );
m_sWhat = m_mExpStringMap.at( Type );
}
BwAdoException( const string& Msg )
:m_sWhat( Msg ) {
}
~BwAdoException() {
}
public:
// 获取错误原因
string GetWhat() const {
return m_sWhat;
}
public:
// 异常类型
enum ExceptionType {
ExpArgNotMatch, // 函数参数个数和要求不匹配
ExpAccessViolation, // 数组访问越界
ExpArgNotDefined, // 给定参数未定义
ExpArgNotValid, // 给定参数无效
};
private:
// 错误信息提示字符映射表
map<int, string> m_mExpStringMap;
string m_sWhat; // 错误原因
};
#endif // !_BW_ADO_H_
AdoHelper.cpp
#include "stdafx.h"
#include "AdoHelper.h"
#include <conio.h>
#include <iostream>
using namespace std;
//
AdoHelper::AdoHelper() {
//
// 完成 COM 初始化
//
::CoInitialize( nullptr );
m_pConnection.CreateInstance( __uuidof( Connection ) );
m_pRecordSet.CreateInstance( __uuidof( Recordset ) );
m_pCommand.CreateInstance( __uuidof( Command ) );
m_pConnection->ConnectionTimeout = 5;
}
//
AdoHelper::~AdoHelper() {
DisconnectDatabase();
// 在 CoUninitialize 中进行了下述操作
// 如果显式完成可能出现错误
// 在调试过程中发现了该隐患
// 所以注释了以下代码
// m_pRecordSet->Release();
// m_pConnection->Release();
// m_pCommand->Release();
::CoUninitialize();
}
//
bool AdoHelper::ConnectDatabase( const int DatabaseType, std::string driver, std::string server,std::string uid,std::string pwd, std::string db) {
//vector<string> arg_vec;
//for_each( List.begin(), List.end(), [&arg_vec]( const string& s ) {arg_vec.push_back( s ); } );
//auto count_of_args = arg_vec.size();
try {
switch ( DatabaseType ) {
case DatabaseType::DatabaseAccess:{
//ConnectionHelper( DatabaseType::DatabaseAccess, arg_vec );
return true;
}
case DatabaseType::DatabaseSQLServer:{
string connect_string = "Provider = SQLOLEDB; Server = "
+ server
+";Initial Catalog="
+ db;
m_pConnection->Open(connect_string.c_str(), uid.c_str(), pwd.c_str(), adModeUnknown);
return true;
}
case DatabaseType::DatabaseMySQL: {
//m_pConnection->Open("DSN=MySQLDSN32;server=106.14.82.242;database=openauthpro", "root", "root", adModeUnknown);
//m_pConnection->Open(connect_string.c_str(), uid.c_str(), pwd.c_str(), adModeUnknown);
//使用Open方法连接数据库(无需配置数据源)
string connect_string = "Provider = MSDASQL.1;Driver="
+driver
+";Server = "
+ server
+ ";DATABASE="
+ db;
//";allowMultiQueries=true";
m_pConnection->Open(connect_string.c_str(), uid.c_str(), pwd.c_str(), adModeUnknown);
return true;
}
case DatabaseType::DatabaseOwn:{
//ConnectionHelper( DatabaseType::DatabaseOwn, arg_vec );
return true;
}
default: {
throw( BwAdoException( BwAdoException::ExpArgNotMatch ) );
}
}
}
catch ( std::out_of_range& e ) {
throw (BwAdoException(e.what()));
}
catch ( _com_error& e ) {
wstring wDisp( e.Description() );
string sDisp;
WideStringToString( wDisp, sDisp );
//throw ( BwAdoException( sDisp ) );
std::cout << sDisp << std::endl;
consoleSQLError->info(sDisp);
consoleSQLError->flush();
}
return false;
}
void AdoHelper::DisconnectDatabase() {
if ( m_pRecordSet->State ) {
m_pRecordSet->Close();
}
if ( m_pConnection->State ) {
m_pConnection->Close();
}
}
//
/*--
函数描述:
在数据库上执行 SQL 语句。
参数:
Command - 待执行的命令。
返回值:
是否执行成功。
++*/
bool AdoHelper::ExcuteSQL( const string& Command ) {
if (m_pConnection==NULL || m_pRecordSet==NULL || m_pCommand==NULL) return false;
try {
if ( m_pRecordSet->State ) {
m_pRecordSet->Close();
}
m_pRecordSet = m_pConnection->Execute( Command.c_str(), nullptr, adCmdText );
m_sCurrentCmd = Command;
if ( m_pRecordSet == nullptr ) {
return false;
}
return true;
}
catch ( _com_error& e ) {
wstring wDisp( e.Description() );
string sDisp;
WideStringToString( wDisp, sDisp );
//throw ( BwAdoException( sDisp ) );
std::cout << sDisp << std::endl;
consoleSQLError->info(sDisp);
consoleSQLError->flush();
}
return false;
}
size_t AdoHelper::WideStringToString( wstring& Source, string& Dest ) {
string curLocale = setlocale( LC_ALL, NULL );
setlocale( LC_ALL, "chs" );
const wchar_t* sSource = Source.c_str();
size_t nDestSize = 2 * Source.size() + 1; // 最后一位 '/0'
size_t nConvertSize{ 0 };
char* sDest = new char[nDestSize];
memset( sDest, 0, nDestSize );
wcstombs_s( &nConvertSize, sDest, nDestSize, sSource, nDestSize );
Dest = sDest;
delete[] sDest;
sDest = nullptr;
setlocale( LC_ALL, curLocale.c_str() );
return nConvertSize;
}
//
size_t AdoHelper::StringToWideString( string& Source, wstring& Dest ) {
string curLocale = setlocale( LC_ALL, NULL );
setlocale( LC_ALL, "chs" );
const char* sSource = Source.c_str();
size_t chDestSize = Source.size() + 1; // cch
size_t nConvertSize{ 0 };
wchar_t* sDest = new wchar_t[chDestSize];
wmemset( sDest, 0, chDestSize );
mbstowcs_s( &nConvertSize, sDest, chDestSize, sSource, chDestSize );
Dest = sDest;
delete[] sDest;
sDest = nullptr;
setlocale( LC_ALL, curLocale.c_str() );
return nConvertSize;
}
bool AdoHelper::GetBigTableValue( const string& ItemName, char* Value, const size_t ValueSize ) {
_variant_t varBLOB;
long lBufLen = 0;
char * p = nullptr;
try {
if ( Value == nullptr ) {
return false;
}
lBufLen = m_pRecordSet->GetFields()->GetItem( ItemName.c_str() )->ActualSize; // 得到数据的长度
varBLOB = m_pRecordSet->GetFields()->GetItem( ItemName.c_str() )->GetChunk( lBufLen ); // 获得数据
if ( varBLOB.vt == ( VT_ARRAY | VT_UI1 ) ) // 判断数据类型是否正确
{
SafeArrayAccessData( varBLOB.parray, (void **)&p ); // 得到指向数据的指针
memcpy_s( Value, ValueSize, p, lBufLen );
SafeArrayUnaccessData( varBLOB.parray );
}
return true;
}
catch ( _com_error& e ) {
wstring wDisp( e.Description() );
string sDisp;
WideStringToString( wDisp, sDisp );
throw ( BwAdoException( sDisp ) );
}
return false;
}
bool AdoHelper::SetBigTableValue( const string& ItemName, char* Value, const size_t ValueSize ) {
bool isSuc{ false };
VARIANT varBLOB;
SAFEARRAY *psa{ nullptr };
SAFEARRAYBOUND rgsabound[1] = { 0 };
try {
if ( Value == nullptr ) {
return false;
}
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = ValueSize;
psa = SafeArrayCreate( VT_UI1, 1, rgsabound ); // 创建SAFEARRAY对象
for ( long i = 0; i < static_cast<long>( ValueSize ); i++ )
SafeArrayPutElement( psa, &i, Value++ ); // 将buf指向的二进制数据保存到SAFEARRAY对象psa中
varBLOB.vt = VT_ARRAY | VT_UI1; // 将varBLOB的类型设置为BYTE类型的数组
varBLOB.parray = psa; // 为varBLOB变量赋值
m_pRecordSet->GetFields()->GetItem( ItemName.c_str() )->AppendChunk( varBLOB ); // 加入BLOB类型的数据
return true;
}
catch ( _com_error& e ) {
wstring wDisp( e.Description() );
string sDisp;
WideStringToString( wDisp, sDisp );
throw ( BwAdoException( sDisp ) );
}
return false;
}