程序啓動,打開數據庫連接,在程序退出前,一直保持這個連接。程序退出時,才關閉該連。
最理想的狀態的前提:該數據庫連接在中間不會斷開。不會由於網絡或者數據庫服務器的原因導致連接斷開。
實際情形是:需要開發一個長時間運行的程序,數據庫服務器可能在很遠的地方,網絡不可靠。人工在連接斷開的時候不可能及時的干預。最好的方式就是程序能夠檢測到連接斷開,並且能夠自動重新連接。
檢測數據庫連接斷開很簡單:在斷開的數據庫連接對象上執行sql語句或者打開數據集會導致數據異常,捕獲該異常時,就可知道數據庫連接斷開了。
在實際重新連接數據時,出現以下問題:
1. CADODatabase m_Database
對應的數據庫連接在程序啓動時連接正常,在重連時,先調用Close方法,再調用Open方法,Open方法直接返回成功,在該連接上執行sql語句,出現異常: 未連接到數據庫。 這說明: 已經連上數據庫對象m_Database在斷開連接,重新Open時,不會真正從新打開連接。
2. 如果在同一個線程中,動態創建CADODatabase 對象,用新對象打開鏈接,同樣存在上面的問題。
由於ADO類實際是COM類,考慮COM對象跟線程的關係,採用如下方法實現在連接斷開的情況下嘗試重新連接。
1. 爲每個數據庫連接對象 CADODatabase 啓動一個線程,作爲該COM對象的線程住所。
2. 數據庫連接對象 CADODatabase 採用動態創建,即每次創建一個線程,同時創建一個新的CADODatabase對象。
例程:
void CTemplateAppToolDlg::InitDB()
{
m_bInitDBThreadExit = false;
g_ObjInitDBThreadParm.pRunClass = this;
g_ObjInitDBThreadParm.pRunFun = InitDBFun;
::CreateThread(NULL,NULL,ThreadRun,(void*)&g_ObjInitDBThreadParm,0,NULL);
}
void CTemplateAppToolDlg::UnintDB()
{
m_bInitDBThreadExit = true;
}
void CTemplateAppToolDlg::InitDBFun(void)
{
std::string Info;
m_DBCenter.SetDBParms(m_DataSource,m_UserID,m_Password);
m_DBCenter.SetDriverType(m_DBDriverType);
m_DBCenter.SetTableParms(m_Table,m_Table2);
m_bDBInit = false;
::CoInitialize(NULL);
while(true)
{
if (m_bInitDBThreadExit)
{
m_DBCenter.Uninit();
::CoUninitialize();
return ;
}
if(m_bDBInit)
{ // 數據庫連接已經成功,等待
Sleep(1000);
continue;
}
//
Info = "連接數據庫1...";
ShowInfo(Info);
if (! m_DBCenter.Init())
{
Info = "數據庫1連接失敗...";
ShowInfo(Info);
m_DBInitTimes += 1;
m_DBCenter.Uninit();
::CoUninitialize();
return;
}
else
{
Info = "數據庫1連接成功...";
ShowInfo(Info);
m_bDBInit = true;
m_DBInitTimes += 1;
}
}
}
CDBCenter 類:
class CDBCenter
{
bool Init();
void Uninit();
...
private:
CADODatabase *m_pDatabase;
}
bool CDBCenter::Init()
{
Uninit();
std::string strConnectString;
if (m_DBDriverType == DB_DRIVER_MS)
{
strConnectString = "Provider=MSDAORA;Data Source=";
}
else
{
strConnectString = "Provider=OraOLEDB.Oracle;Data Source=";
}
strConnectString += m_strDataSource.c_str();
strConnectString += ";";
m_pDatabase = new CADODatabase();
m_pDatabase->SetConnectionTimeout(30);
BOOL rs;
rs = m_pDatabase->Open(strConnectString.c_str(),m_strUserID.c_str(),m_strPassword.c_str());
if (rs == FALSE)
return false;
m_pDatabase->m_pConnection->PutCommandTimeout(30);
//
std::string strSql = "select * from all_tables where rownum < 2";
bool success = (TRUE == m_pDatabase->Execute(strSql.c_str()));
TRACE("Init return %d/r/n",success ? "true" : "false");
return success;
}
void CDBCenter::Uninit()
{
if (m_pDatabase != NULL)
{
// m_pDatabase->Close();
delete m_pDatabase;
m_pDatabase = NULL;
}
}
主工作線程對數據庫連接的重連操作:
void CTemplateAppToolDlg::Process(void)
{
m_bProcessRunning = true;
std::string Info;
Info = "工作線程啓動...";
ShowInfo(Info);
LabCheckDB:
dbError = false;
// 通過該數據庫連接執行數據庫操作
exist = m_DBCenter.ReadData(value, dbError);
if(dbError)
{
Info = "數據庫1錯誤:數據集打開失敗...";
ShowInfo(Info);
}
while(dbError)
{
if (m_bProcessThreadExit)
{
break;
}
// 數據庫1 初始化線程退出 重新初始化
UnintDB();
Sleep(2000);
m_bDBInit = false;
m_bInitDBThreadExit = false;
m_DBInitTimes = 0;
// 啓動數據庫連接線程
InitDB();
while(m_DBInitTimes < 1)
{
// 嘗試一次前 一直等待
Sleep(1000);
}
// 嘗試一次 判斷是否連上
if (! m_bDBInit)
{
// 沒有連上 重新連接
Sleep(1000);
continue;
}
// 已經連上,重新執行上面的數據庫操作
goto LabCheckDB;
}