ADO 資料庫連線斷開重連

edward22發表於2011-01-05
     在使用ADO的過程中,最理想的操作流程:

    程式啟動,開啟資料庫連線,在程式退出前,一直保持這個連線。程式退出時,才關閉該連。

    最理想的狀態的前提:該資料庫連線在中間不會斷開。不會由於網路或者資料庫伺服器的原因導致連線斷開。

    

    實際情形是:需要開發一個長時間執行的程式,資料庫伺服器可能在很遠的地方,網路不可靠。人工在連線斷開的時候不可能及時的干預。最好的方式就是程式能夠檢測到連線斷開,並且能夠自動重新連線。

    

    檢測資料庫連線斷開很簡單:在斷開的資料庫連線物件上執行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;
   }

相關文章