VC++ ADO数据库断线重连

项目背景:

该项目为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 
#include 
#include 
#include 
#include 
#include 

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
	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 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 m_mExpStringMap;

	string m_sWhat;				// 错误原因
};
#endif // !_BW_ADO_H_

AdoHelper.cpp

#include "stdafx.h"
#include "AdoHelper.h"
#include 
#include 
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 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( 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;
}

你可能感兴趣的:(数据库)