【数据库开发】如何创建MySQL数据库连接池(一个基于libmysql的MySQL数据库连接池示例(C/C++版))

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 已经初始化
");
		}
		return DATABASE_NOERROR;
	}

	m_pMySql = mysql_init((MYSQL*)NULL);

	if (m_pMySql == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Fatal( __FILE__, __LINE__, "m_pMySql 初始化失败
");
		}
		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
", GetLastErrorMsg());
		}
		int iRet = mysql_errno(m_pMySql);
		printf( "errno = %d
", 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
", 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 没有初始化
");
		}
		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 没有初始化
");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	if (mysql_select_db(m_pMySql,szDB))
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "选择数据库失败,%s
", 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 没有初始化
");
		}
		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 没有初始化
");
		}
		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 没有初始化
");
		}
		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 没有初始化
");
		}
		return NULL;
	}
	if(mysql_query(m_pMySql, szSql))
	{
		if( NULL != pLC )
		{
			pLC->Error( __FILE__, __LINE__, "读数据库失败,%s
", 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 没有初始化
");
		}
		return DATABASE_ERROR_INIT_POOL;
	}
	if(mysql_query(m_pMySql,szSql))
	{
		if( NULL != pLC )
      	{
         	pLC->Error( __FILE__, __LINE__, "SQL语句执行失败,%s
", 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 没有初始化
");
      }

		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] = '';
      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
/// rief DATABASE数据库结果集接口
/// author hkp(horace20@live.cn) 
/// 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:
		/////////////////////////////////////////////////////////////////////////////////
		/// rief CDBResultSet类的构造函数
		/// param[in]  无
		/// param[out]/// 
eturn     无
		/////////////////////////////////////////////////////////////////////////////////
		CDBResultSet();

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 释放结果集,得到字段的个数
		/// param[in]  结果集
		/// param[out]/// 
eturn     无
		/////////////////////////////////////////////////////////////////////////////////      
		void Reset(MYSQL_RES *pMyQuery);

	public:
		/////////////////////////////////////////////////////////////////////////////////
		/// rief CDBResultSet类的析构函数
		/// param[in]  结果集
		/// param[out]/// 
eturn     无
		/////////////////////////////////////////////////////////////////////////////////      
		~CDBResultSet(){ Close(); }

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 是否最后
		/// param[in] 无
		/// param[out]/// 
eturn     TRUE 到最后  FALSE 没到最后 
		/////////////////////////////////////////////////////////////////////////////////      		
		bool IsEnd();                
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 查找指定数据
		/// param[in] 偏移量
		/// param[out]/// 
eturn     无
		/////////////////////////////////////////////////////////////////////////////////      		
		void SeekData(int offset);  
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 释放结果集
		/// param[in] 无
		/// param[out]/// 
eturn     无
		/////////////////////////////////////////////////////////////////////////////////      		
 		void Close();                         

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到记录数
		/// param[in] 无
		/// param[out]/// 
eturn     >=0 正常  <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		DB_ROW_CONUT GetRowNum();                      

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 取得下一条记录
		/// param[in] 无
		/// param[out]/// 
eturn     TRUE 正常  FALSE 异常
		/////////////////////////////////////////////////////////////////////////////////      		
		bool GetNextRecod();

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得有符号整型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, int& value);           

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得无符号整型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, unsigned int& value);   

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得字符串类型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, string& value);     
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得len长度的字符串值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, char* value, int len);  

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得bool值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, bool& value); 
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得float值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, float& value);  
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得double值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, double& value);        
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得时间类型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(int filedId, time_t& value);        

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
		/// param[in] 字段下标
		/// param[out] 指向二进制buffer的指针
		/// param[out] 指针的长度
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////
        int GetFiled(int filedId, uint8 **value, int *len);
    	
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段名
		/// param[out] 取得有符号整型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, int& value);            

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段下标
		/// param[out] 取得wu符号整型值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////      		
		int GetFiled(const char* fieldName, unsigned int& value); 

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

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

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

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段
		/// param[in] 字段名
		/// param[out] 取得double值
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////  
		int GetFiled(const char* fieldName, double& value);    
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
		/// param[in]  字段下标
		/// param[out] 指向二进制buffer的指针
		/// param[out] 指针的长度
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////
		int GetFiled(const char* fieldName, time_t& value);     

		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
		/// param[in] 字段名
		/// param[out] 指向二进制buffer的指针
		/// param[out] 指针的长度
		/// 
eturn     =0 正常 <0异常
		/////////////////////////////////////////////////////////////////////////////////
        int GetFiled(const char* fieldName, uint8 **value, int *len);

	private:
      
		/////////////////////////////////////////////////////////////////////////////////
		/// rief 得到字段ID
		/// param[in] 字段名
		/// param[out] 取得字段的ID
		/// 
eturn     >=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 未初始化
");
		}
		m_fieldCount = 0;
	}
}

bool CDBResultSet::IsEnd()
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化
");
		}
		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 未初始化
");
		}
		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 未初始化
");
		}
		return false;
	}
	if( (m_currentRow = mysql_fetch_row(m_pMyQuery)) != NULL)
	{
		return true;
	}
	else
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "m_currentRow 为空
");
		}
		return false;
	}
}

DB_ROW_CONUT CDBResultSet::GetRowNum()
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化
");
		}
		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 未初始化或下标越界
");
		}
		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 未初始化或下标越界
");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Trace( __FILE__, __LINE__, "字段为空
");
		}
		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 未初始化或下标越界
");
		}
		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 未初始化或下标越界
");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	if (m_currentRow[filedId] == NULL)
	{
		value[0] = '';
	}
	else
	{
		strncpy(value, m_currentRow[filedId], len-1);
		value[len-1] = '';
	}
	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 未初始化或下标越界
");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段为空
");
		}
		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 未初始化或下标越界
");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段为空
");
		}
		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 未初始化或下标越界
");
		}
		return DATABASE_ERROR_GET_FIELDS;
	}
	char* pField = m_currentRow[filedId];
	if (pField == NULL || pField[0] == 0)
	{
		if( NULL != pLC )
		{
			pLC->Warn( __FILE__, __LINE__, "字段为空
");
		}
		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 未初始化或下标越界
");
		}
		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__, "字段为空
");
		}
		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 未初始化或下标越界
");
      }
      return DATABASE_ERROR_GET_FIELDS;
   }

   if (m_currentRow[filedId] == NULL)
   {
      if( NULL != pLC )
      {
         pLC->Trace( __FILE__, __LINE__, "此字段为空
");   
         *len = 0;  
         //*value = new uint8[1];
         *value = (uint8 *)DATABASE_ALLOC(sizeof(uint8));
         if (*value == NULL)
         {
            if( NULL != pLC )
            {
               pLC->Trace( __FILE__, __LINE__, "分配内存失败
");
            }    
            return DATABASE_ERROR_ALLOC_BUFFER;
         }
         (*value)[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__, "分配内存失败
");
         }      

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

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

    return DATABASE_NOERROR;
}           


int CDBResultSet::GetFiedIdByName(const char* fieldName)
{
	if (m_pMyQuery == NULL)
	{
		if( NULL != pLC )
		{
			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化
");
		}
		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__, "未找到该字段
");
	}

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


         
原文地址:https://www.cnblogs.com/huty/p/8517468.html