如何創建數據庫連接池(一個基於libmysql的MySQL數據庫連接池示例)

1、  一般架構說明

1 架構層次圖

        一般應用系統數據庫訪問模塊可大致分爲兩層,一層是對數據庫連接、連接池和結果集等直接對數據庫的操作的封裝,由於libmysql提供的庫函數是直接操作數據庫的,所以這一層在本質上是直接操作數據庫的一層;二是可以根據系統業務功能將系統與數據庫的交互劃分爲幾個子塊,提供給系統其它模塊與數據庫交互的接口。如果是C/S結構的系統,客戶端與數據庫的交互還可以通過諸如RPC(Remote Procedure Call Protocol 遠程過程調用協議)等協議調用服務端RPC處理模塊來進行。

        如上的設計一是隔離了外界與數據庫的直接交互,提高了數據安全性;二是對libmysql提供的數據庫操作接口衆多,統一類操作有多種不同的方式,各自適用的場合不同,對其進行了整合,是爲使其更適合本系統,提高了系統的穩定性和可用性,同時使上層對數據庫的操作更爲方便;三是按系統功能劃分子模塊降低了系統的耦合度,提高了系統的可擴展性。

        另外,數據庫直接交互層也分爲三個小塊,這樣做的目的也是爲了降低系統耦合度。其中數據庫連接管理塊僅負責數據庫連接的維持及相應查詢事物處理。數據庫連接池管理塊則是負責在系統初始化時創建一定數量的數據庫連接,實際上就是建立兩個連接隊列(一個在用連接隊列和一個空閒連接隊列)並維護這兩個隊列。對於連接池的建立是爲了避免在每一次操作數據庫時都要建立數據庫連接和釋放數據庫連接等耗時操作,提高系統性能。數據結果集的處理是專門負責將查詢返回的結果按上層要求的方式提供給上層使用。

2、  數據庫連接類的實現

        本類主要是實現數據庫連接的建立和釋放,數據庫選擇,SQL語句的執行,事務的提交和回滾及數據庫錯誤信息的獲取等功能。其中數據庫連接的建立與釋放及查詢語句的執行是本節敘述的重點。在libmysql提供的庫中使用mysql_real_connect()可與MySQL數據庫建立連接,但在這之前還需要對MYSQL實例進行初始化,其中MYSQL爲MySQL的數據庫連接句柄,具體實現過程如下。

        a、使用mysql_init()創建MYSQL句柄實例;

        b、根據數據庫主機地址、用戶名、用戶密碼、數據庫名及端口號等連接信息使用mysql_real_connect()接口爲MYSQL句柄實例建立數據庫連接;

        c、爲已經建立數據庫連接的MYSQL句柄實例選擇字符集。

        需要注意的是,當應用程序服務器和數據庫服務器不在同一主機時,新安裝的MySQL數據庫處於安全考慮是不允許遠程連接的,這就需要爲相應的用戶賦予遠程訪問的權限,可採用MySQL命令:
GRANT EXECUTE ON DBName.TableName TO 'UserName'@'192.168.1.1' IDENTIFIED BY 'UserPassword';

        其中“192.168.1.1”爲遠程主機的IP地址,使用“%”表示接受所有IP主機的遠程訪問。當然出於數據安全考慮,需要慎重執行。最終實現連接建立的代碼如下:
int CDBConnect::ConnectDB(const char *host, const char *user, const char *password, const char *db, unsigned int port)
{
	if (m_pMySql != NULL)
	{
		if( NULL != pLC )
		{
			pLC->Trace( __FILE__, __LINE__, "m_pMySql 已經初始化\n");
		}
		return DATABASE_NOERROR;
	}

	m_pMySql = mysql_init((MYSQL*)NULL);

	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Fatal( __FILE__, __LINE__, "m_pMySql 初始化失敗\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	
	if (!mysql_real_connect(m_pMySql, host, user, password, db, port,NULL,0))
	{
		if( NULL != pLC )
		{
			pLC->Fatal( __FILE__, __LINE__, "數據庫連接失敗,%s\n", GetLastErrorMsg());
		}
		int iRet = mysql_errno(m_pMySql);
		printf( "errno = %d\n", iRet );
		if (iRet == 1045)
		{  
			return DATABASE_ERROR_USERORPASSWORD;
		}
		return DATABASE_ERROR_GET_CONNECTION;
	}

	if (mysql_set_character_set(m_pMySql, "gbk") != 0) 
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "數據庫字符集設置失敗,%s\n", GetLastErrorMsg());
		}
		return DATABASE_POOL_ERROR_SET_CHARACTER;
	}

	return DATABASE_NOERROR;
}

void CDBConnect::Release()
{
	delete this;
}

void CDBConnect::CloseConnect()
{
	m_ResultSet.Close();

	if (m_pMySql != NULL)
	{
		mysql_close(m_pMySql);
		m_pMySql = NULL;
	}
}

void CDBConnect::CloseQuery()
{
	m_ResultSet.Close();
}

int CDBConnect::ConnctionTest()
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	else
	{
		return mysql_ping(m_pMySql); 
	}
}

int CDBConnect::SelectDB(const char *szDB)
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	if (mysql_select_db(m_pMySql,szDB))
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "選擇數據庫失敗,%s\n", GetLastErrorMsg());
		}
		return -1/*DATABASE_POOL_ERROR_SELECT_DATABASE*/;
	}
	else
	{
		return DATABASE_NOERROR;
	}
}

int CDBConnect::AutoCommit(bool mode)
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	return mysql_autocommit(m_pMySql, mode);
}

int CDBConnect::Commite()
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	return mysql_commit(m_pMySql); 
}

int CDBConnect::RollBack()
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	return mysql_rollback(m_pMySql); 
}

PIDBResultSet CDBConnect::ExcuteQuery(const char *szSql)
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return NULL;
	}
	if(mysql_query(m_pMySql, szSql))
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "讀數據庫失敗,%s\n", GetLastErrorMsg());
		}
		return NULL;
	}
	m_ResultSet.Reset(mysql_store_result(m_pMySql));	
	return &m_ResultSet;
}

int CDBConnect::ExcuteSql(const char *szSql)
{
	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	if(mysql_query(m_pMySql,szSql))
	{
		if( NULL != pLC )
      	{
         	pLC->Error( __FILE__, __LINE__, "SQL語句執行失敗,%s\n", GetLastErrorMsg());
		}
		return DATABASE_ERROR_EXCUTE_QUERY;
	}
	return mysql_affected_rows(m_pMySql);
}


int CDBConnect::ExcuteRealSql(const char *szSql, unsigned long ulSqlLen )
{
	//這裏添加參數檢查語句
	if( ulSqlLen <= 0 )
	{
		//這裏添加日誌打印語句

	}
	if (m_pMySql == NULL)
	{
		//這裏添加日誌打印語句
		return DATABASE_ERROR_EXCUTE_QUERY;
	}
	if(mysql_real_query(m_pMySql,szSql, ulSqlLen))
	{
		//這裏添加日誌打印語句
		return DATABASE_ERROR_EXCUTE_QUERY;
	}
	return mysql_affected_rows(m_pMySql);
}

const char* CDBConnect::GetLastErrorMsg()
{
	if (m_pMySql != NULL)
	{
		return mysql_error(m_pMySql);
	}
	else
	{
		return NOT_CONNECT_DATABASE;
	}
}

int CDBConnect::GetLastInsertID(int *pInsertID)
{
	if (m_pMySql != NULL)
	{
		*pInsertID = mysql_insert_id(m_pMySql);

		return DATABASE_NOERROR;
	}
	else
	{
      if( NULL != pLC )
      {
         pLC->Debug( __FILE__, __LINE__, "m_pMySql 沒有初始化\n");
      }

		return DATABASE_ERROR_INIT_POOL;
	}
}

unsigned long CDBConnect::EscapeRealToString(  char *pDst, const char *pSrc, unsigned long ulSrcLen )
{
	//這裏添加參數檢查代碼…
	if( ulSrcLen <= 0 )
	{
		//這裏添加日誌打印語句
	}

	if (m_pMySql == NULL)
	{
		//這裏添加日誌打印語句
		return DATABASE_ERROR_INIT_POOL;
	}
	return mysql_real_escape_string(m_pMySql,pDst, pSrc, ulSrcLen);
}

 3、  數據庫連接池類的實現

        本類實現了連接池的建立和銷燬,連接池運行狀態信息的獲取,從連接池中取出可用數據庫連接及將短時間內不再使用的數據庫連接放回連接池等操作。本類的架構實現關係到數據庫的訪問速度,攸關整個系統性能。同時爲了維護連接池的安全性,保證在同一時間內只有一個線程能操作連接池,同一個連接也只能被一個線程使用,涉及到設置臨界區和信號量等操作,下面將逐一詳述。

        a、數據結構描述
typedef std::list<PIDBConnect> CONNECTION_HANDLE_LIST;
typedef std::list<PIDBConnect>::iterator ITER_CONNECTION_HANDLE_LIST;
class CDBConnectPool
{
private:
string m_host;           //主機
string m_user;           //用戶名
string m_password;       //密碼
string m_db;             //數據庫名
unsigned int m_port;     //端口
unsigned int m_connNum;  //連接池的大小
      
CONNECTION_HANDLE_LIST m_lsBusyList; //正在使用的連接句柄,隊列模型
CONNECTION_HANDLE_LIST m_lsIdleList; //未使用的連接句柄,隊列模型
		
CRITICAL_SECTION m_csList;            //臨界值句柄
HANDLE           m_hSemaphore;        //信號量句柄
static CDBConnectPool *pConnPool;     //單例模式
}

        b、連接池的建立

        在連接池建立時,首先應跟據用戶要求建立連接的數量創建相應數量信號量,在逐個新建連接並放入空閒連接隊列,其中任何一個連接創建失敗都將導致整個連接池的建立失敗。具體實現如下:
int CDBConnectPool::Connect()
{
	//同時打開connNum個連接
	int ret = 0;
	if (m_hSemaphore != NULL)
	{
		return DATABASE_ERROR;
	}
	m_hSemaphore = CreateSemaphore(NULL, m_connNum, m_connNum, NULL);
	if (m_hSemaphore == NULL)
	{
		return DATABASE_POOL_ERROR_INIT_SEMAPHORE;
	}
   int i = 0;
	for ( i=0; i<m_connNum; ++i)
	{
		CDBConnect *pConn = new CDBConnect();
		ret = pConn->ConnectDB(m_host.c_str(), m_user.c_str(), m_password.c_str(), m_db.c_str(), m_port);
		if(ret<0)
		{
			break;
		}
      	m_lsIdleList.push_back(pConn);
	}
	if (i != m_connNum)
	{
		Close();
		if (ret == DATABASE_ERROR_USERORPASSWORD)
		{
			return ret;
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	return DATABASE_NOERROR;
}

        c、關閉連接池

        連接池的關閉與創建過程恰好相反,首先應關閉所有數據庫連接,包括在用的和未用的,再清空隊列(空閒連接隊列和在用連接隊列),最後清除信號量。實現代碼如下:
void CDBConnectPool::Close()
{
	ITER_CONNECTION_HANDLE_LIST iter;
	for (iter = m_lsBusyList.begin(); iter != m_lsBusyList.end(); iter++)
	{
		PIDBConnect pConn = *iter;
		if( pConn != NULL )
		{
			pConn->CloseConnect();
			pConn->Release();
		}
	}
	m_lsBusyList.clear();	
	for (iter = m_lsIdleList.begin(); iter != m_lsIdleList.end(); iter++)
	{
		PIDBConnect pConn = *iter;
		if( pConn != NULL )
		{
			pConn->CloseConnect();
			pConn->Release();
		};
	}
	m_lsIdleList.clear();
	if( m_hSemaphore != NULL )
	{
		CloseHandle(m_hSemaphore);
		m_hSemaphore = NULL;
	}
}

        d、從連接池中獲取可用連接

        在從連接池中取出連接時,首先等待信號量,直到有可用連接或者等待超時返回(避免線程死鎖),然後進入臨界區,即對兩個隊列的操作區域,防止在取連接時有其它線程也來去連接或是放連接回池,接着離開臨界區,通知其它等待線程可操作臨界區。另外比較重要的一點是,爲確保我們取到的連接是真正可用的連接,在從空閒連接隊列中取出連接並放入忙連接隊列後需要測試我們得到的連接是否連通,未連通時應嘗試重連,若重連依然失敗,則應返回空。實現代碼如下:
PIDBConnect CDBConnectPool::GetConnection(int iTimeOut)
{
   int ret;
	PIDBConnect pConn = NULL;
	DWORD dwWaitResult;

	dwWaitResult = WaitForSingleObject(m_hSemaphore, iTimeOut);
	if (dwWaitResult != WAIT_OBJECT_0)
	{
		return NULL;
	}
	EnterCriticalSection(&m_csList);
	if (m_lsIdleList.size() > 0)
	{
		pConn = m_lsIdleList.front();
		m_lsIdleList.pop_front();
		m_lsBusyList.push_back(pConn);
	}
	else
	{
		pConn = NULL;
	}
	LeaveCriticalSection(&m_csList);

	if (pConn->ConnctionTest() != 0)
   {
      char strError[1000] = {0};
      strncpy(strError, pConn->GetLastErrorMsg(), 999);
      strError[999] = '\0';
      pConn->CloseConnect();
      ret = pConn->ConnectDB(m_host.c_str(), m_user.c_str(), 
						m_password.c_str(), m_db.c_str(), m_port);
      if (ret < 0)
      {
         BackToPool(pConn);
         return NULL;
      }
   }
	return pConn;
}

        e、將未用連接放回連接池
void CDBConnectPool::BackToPool(PIDBConnect pConn)
{
	pConn->CloseQuery();
    pConn->AutoCommit(true);
	EnterCriticalSection(&m_csList);
	ReleaseSemaphore(m_hSemaphore, 1, NULL); 
	m_lsBusyList.remove(pConn);
	m_lsIdleList.push_back(pConn);
	LeaveCriticalSection(&m_csList);
}

4、  數據庫結果集類的實現

        本類主要實現對查詢返回結果的一系列操作,包含查找指定數據、取得記錄數、獲得下一條記錄及根據字段名或是字段在結果集中的ID獲取字段等。直接給出源碼如下:

頭文件:
/////////////////////////////////////////////////////////////////////////////////
/// Copyright (C), 2011
/// \file  DBResultSet.h
/// \brief DATABASE數據庫結果集接口
/// \author hkp([email protected]) 
/// \version 1.1
/// \date    2011
///////////////////////////////////////////////////////////////////////////////////


#ifndef _DATABASE_RECORD_SET_
#define _DATABASE_RECORD_SET_

#pragma comment(lib,"libmysql.lib")
#pragma warning(disable: 4786)     ///<Disable warning messages

#include <string>
#include <map>

using namespace std;

namespace Database
{
	class CDBResultSet : public IDBResultSet
	{
		friend class CDBConnect;    ///<友元類
      
	private:
		MYSQL_RES*   m_pMyQuery;    ///<結果集
		MYSQL_ROW    m_currentRow;  ///<基類
		unsigned int m_fieldCount;  ///<字段的個數

	private:
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief CDBResultSet類的構造函數
		/// \param[in]  無
		/// \param[out] 無
		/// \return     無
		/////////////////////////////////////////////////////////////////////////////////
		CDBResultSet();

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 釋放結果集,得到字段的個數
		/// \param[in]  結果集
		/// \param[out] 無
		/// \return     無
		/////////////////////////////////////////////////////////////////////////////////      
		void Reset(MYSQL_RES *pMyQuery);

	public:
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief CDBResultSet類的析構函數
		/// \param[in]  結果集
		/// \param[out] 無
		/// \return     無
		/////////////////////////////////////////////////////////////////////////////////      
		~CDBResultSet(){ Close(); }

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 是否最後
		/// \param[in] 無
		/// \param[out] 無
		/// \return     TRUE 到最後  FALSE 沒到最後 
		/////////////////////////////////////////////////////////////////////////////////      		
		bool IsEnd();                
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 查找指定數據
		/// \param[in] 偏移量
		/// \param[out] 無
		/// \return     無
		/////////////////////////////////////////////////////////////////////////////////      		
		void SeekData(int offset);  
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 釋放結果集
		/// \param[in] 無
		/// \param[out] 無
		/// \return     無
		/////////////////////////////////////////////////////////////////////////////////      		
 		void Close();                         

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到記錄數
		/// \param[in] 無
		/// \param[out] 無
		/// \return     >=0 正常  <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		DB_ROW_CONUT GetRowNum();                      

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 取得下一條記錄
		/// \param[in] 無
		/// \param[out] 無
		/// \return     TRUE 正常  FALSE 異常
		/////////////////////////////////////////////////////////////////////////////////      		
		bool GetNextRecod();

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得有符號整型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, int& value);           

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得無符號整型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, unsigned int& value);   

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得字符串類型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, string& value);     
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得len長度的字符串值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, char* value, int len);  

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得bool值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, bool& value); 
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得float值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, float& value);  
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得double值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, double& value);        
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得時間類型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, time_t& value);        

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段,此函數分配內存,調用FreeGetFiledByte(BYTE **value)釋放內存
		/// \param[in] 字段下標
		/// \param[out] 指向二進制buffer的指針
		/// \param[out] 指針的長度
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////
        int GetFiled(int filedId, uint8 **value, int *len);
    	
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得有符號整型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, int& value);            

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段下標
		/// \param[out] 取得wu符號整型值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, unsigned int& value); 

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得字符串值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, string& value);
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得len長度的字符串值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, char* value, int len); 

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得bool值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////  
		int GetFiled(const char* fieldName, bool& value);  

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得float值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////  
		int GetFiled(const char* fieldName, float& value);         

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段
		/// \param[in] 字段名
		/// \param[out] 取得double值
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////  
		int GetFiled(const char* fieldName, double& value);    
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段,此函數分配內存,調用FreeGetFiledByte(BYTE **value)釋放內存
		/// \param[in]  字段下標
		/// \param[out] 指向二進制buffer的指針
		/// \param[out] 指針的長度
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////
		int GetFiled(const char* fieldName, time_t& value);     

		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段,此函數分配內存,調用FreeGetFiledByte(BYTE **value)釋放內存
		/// \param[in] 字段名
		/// \param[out] 指向二進制buffer的指針
		/// \param[out] 指針的長度
		/// \return     =0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////
        int GetFiled(const char* fieldName, uint8 **value, int *len);

	private:
      
		/////////////////////////////////////////////////////////////////////////////////
		/// \brief 得到字段ID
		/// \param[in] 字段名
		/// \param[out] 取得字段的ID
		/// \return     >=0 正常 <0異常
		/////////////////////////////////////////////////////////////////////////////////   
		int GetFiedIdByName(const char* fieldName);
	};
}
#endif

CPP文件:
CDBResultSet::CDBResultSet()
{
	m_pMyQuery   = NULL;
	m_currentRow = NULL;
	m_fieldCount = 0;
}

void CDBResultSet::Reset(MYSQL_RES *pMyQuery)
{
	Close();
	m_pMyQuery   = pMyQuery;
	m_currentRow = NULL;
	if (pMyQuery != NULL)
	{
		m_fieldCount = mysql_num_fields(pMyQuery);
	}
	else
	{
		if( NULL != pLC )
		{
			pLC->Trace( __FILE__, __LINE__, "pMyQuery 未初始化\n");
		}
		m_fieldCount = 0;
	}
}

bool CDBResultSet::IsEnd()
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
		}
		return false;
	}
	return mysql_eof(m_pMyQuery) != 1;
}

void CDBResultSet::SeekData(int offset)
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
		}
		return;
	}
	mysql_data_seek(m_pMyQuery, offset);
}

void CDBResultSet::Close()
{
	if (m_pMyQuery == NULL)
	{
		return;
	}
	mysql_free_result(m_pMyQuery);
	m_pMyQuery   = NULL;
	m_currentRow = NULL;
	m_fieldCount = 0;
}

bool CDBResultSet::GetNextRecod()
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
		}
		return false;
	}
	if( (m_currentRow = mysql_fetch_row(m_pMyQuery)) != NULL)
	{
		return true;
	}
	else
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "m_currentRow 爲空\n");
		}
		return false;
	}
}

DB_ROW_CONUT CDBResultSet::GetRowNum()
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化\n");
		}
		return DATABASE_NOERROR;
	}
	return mysql_num_rows(m_pMyQuery);
}

int CDBResultSet::GetFiled(int filedId, int& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		value = 0;
		return DATABASE_ERROR_NEXT_ROW;
	}
	value = atoi(pField);
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, unsigned int& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Trace( __FILE__, __LINE__, "字段爲空\n");
		}
		value = 0;
		return DATABASE_ERROR_NEXT_ROW;
	}
	value = atoi(pField);
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, string& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	value = pField;
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, char* value, int len)  
{
	if (m_currentRow == NULL || filedId>=m_fieldCount || value == NULL || len < 0)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	if (m_currentRow[filedId] == NULL)
	{
		value[0] = '\0';
	}
	else
	{
		strncpy(value, m_currentRow[filedId], len-1);
		value[len-1] = '\0';
	}
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, bool& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段爲空\n");
		}
		value = false;
		return DATABASE_ERROR_NEXT_ROW;
	}
	value = atoi(pField) != 0;
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, float& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段爲空\n");
		}
		value = 0.0;
		return DATABASE_ERROR_NEXT_ROW;
	}
	value = atof(pField);
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, double& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段爲空\n");
		}
		value = 0.0;
		return DATABASE_ERROR_NEXT_ROW;
	}
	value = atof(pField);
	return DATABASE_NOERROR;
}

int CDBResultSet::GetFiled(int filedId, time_t& value)
{
	if (m_currentRow == NULL || filedId>=m_fieldCount)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	tm time;
	ZeroMemory(&time, sizeof(time));
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段爲空\n");
		}
		value = 0;
		return DATABASE_ERROR_NEXT_ROW;
	}
	sscanf(pField, "%d-%d-%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec);
	time.tm_year -= 1900;
	time.tm_mon  -= 1;
	value = mktime(&time);
	return DATABASE_NOERROR;
}

///<函數功能:得到字段
int CDBResultSet::GetFiled(int filedId, uint8 **value, int *len)
{
   *len = 0;
   *value = NULL;
   if (m_currentRow == NULL || filedId>=m_fieldCount)
   {
      if( NULL != pLC )
      {
         pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下標越界\n");
      }
      return DATABASE_ERROR_GET_FIELDS;
   }

   if (m_currentRow[filedId] == NULL)
   {
      if( NULL != pLC )
      {
         pLC->Trace( __FILE__, __LINE__, "此字段爲空\n");   
         *len = 0;  
         //*value = new uint8[1];
         *value = (uint8 *)DATABASE_ALLOC(sizeof(uint8));
         if (*value == NULL)
         {
            if( NULL != pLC )
            {
               pLC->Trace( __FILE__, __LINE__, "分配內存失敗\n");
            }    
            return DATABASE_ERROR_ALLOC_BUFFER;
         }
         (*value)[0] = '\0';
      }
   }
   else
   {

      unsigned long * plLen = mysql_fetch_lengths(m_pMyQuery); //讀該字段的長度,含0值數據
      *len = (int)*plLen;
      *value = (uint8 *)DATABASE_ALLOC(sizeof(uint8)*(*len) + 1);
      if (*value == NULL)
      {
         *len = 0;
         if( NULL != pLC )
         {
            pLC->Trace( __FILE__, __LINE__, "分配內存失敗\n");
         }      

         return DATABASE_ERROR_ALLOC_BUFFER;
      }
      
      for (int i = 0; i < *len; i++)
      {
         (*value)[i] = (uint8)m_currentRow[filedId][i];
      }

      (*value)[*len] = '\0';
   }

    return DATABASE_NOERROR;
}           


int CDBResultSet::GetFiedIdByName(const char* fieldName)
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	for (int i=0; i<m_fieldCount; i++)
	{
		MYSQL_FIELD *pField = mysql_fetch_field_direct(m_pMyQuery, i);
		if (pField != NULL && strcmp(pField->name, fieldName) == 0)
		{
			return i;
		}
	}
	if( NULL != pLC )
	{
		pLC->Trace( __FILE__, __LINE__, "未找到該字段\n");
	}

	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, int& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, unsigned int& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, string& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

 	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, char* value, int len)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value, len);
	}

 	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, bool& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, float& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

   return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, double& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, time_t& value)
{
	int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value);
	}

 	return DATABASE_ERROR_NEXT_ROW;
}

int CDBResultSet::GetFiled(const char* fieldName, uint8 **value, int *len)
{
   int id = 0;
	if ((id=GetFiedIdByName(fieldName)) >= 0)
	{
		return GetFiled(id, value, len);
	}

    return DATABASE_NOERROR;
} 

5、  總結

        總結就不必了,本文出自個人本科畢設摘抄整理,未經允許不得轉載,如需轉載請聯繫[email protected],如若學校說我論文是抄別人的,那我就冤枉了……
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章