解决了绑定变量问题后,剩下的事情就简单多了。
设想的工作模式是这样的:
目的:解决mysql的常用访问。
经常用的是单行的SELECT,以及多行的获取。
而多条记录的SELECT,往往是field相同,可以有一个或者多个结果集。
多个不同field的结果集访问非常少见,可以通过写程序稍加控制解决。
手段:
结果的获取,可以使用可变参数...(貌似C#没有这个?不记得了),而首先可以先把返回结果全部定义成为tResult类,然后取得结果后,再转换成为相应想使用的类型。
int iId,iBaseType; System::String^ fciname,^fciremark; array<tResult^>^ m_result; m_result = gcnew array<tResult^>(4); for(i=0;i<4;i++) m_result[i] = gcnew tResult(); m_sql->ProcessRow2(pRow,m_result[0],m_result[1],m_result[2],m_result[3]); iId = m_result[0]; iBaseType = m_result[1]; fciname = m_result[2]; fciremark = m_result[3];
整个遍历过程:
Execute(执行SQL语句) while(FetchRow == true) { ProcessRow(获得结果) FetchRow }
测试代码如下:
void TestConnect() { tsql^ m_sql; int^ m_iRet; bool m_bRet; int i; tRow^ pRow=nullptr; System::String^ m_sRet; array<tResult^>^ m_result; m_result = gcnew array<tResult^>(4); for(i=0;i<4;i++) m_result[i] = gcnew tResult(); // 1. 创建SQL类实例 m_sql = gcnew tsql(); // 2. 连接数据库 m_bRet = m_sql->ConnectDB(L"127.0.0.1","root","","test",3306,m_iRet,m_sRet); Console::WriteLine("Connect result:{0},{1}",m_iRet,m_sRet); m_sRet = Console::ReadLine(); // 3. 设定字符集为gbk m_bRet = m_sql->Execute(m_iRet,m_sRet," set names gbk"); Console::WriteLine("set names gbk:result:{0},{1}",m_iRet,m_sRet); // 4. 执行单行SELECT m_bRet = m_sql->Select(m_iRet,m_sRet," select count(*) from TCI_TYPEBASEINFO ",m_result[0]); Console::WriteLine("Select result:{0},{1}",m_iRet,m_sRet); if (m_bRet == true) { int iCount; iCount = m_result[0]; Console::WriteLine(" count(*) of TCI_TYPEBASEINFO is:{0}",iCount); } m_sRet = Console::ReadLine(); // 5. 用EXECUTE,FECHROW,PROCESSROW来遍历多条记录 m_bRet = m_sql->Execute(m_iRet,m_sRet, " select FCITYPEID,FCIBASETYPE,FCINAME,FCIREMARK from TCI_TYPEBASEINFO "); Console::WriteLine("Execute result:{0},{1}",m_iRet,m_sRet); m_bRet = m_sql->FetchRow(m_iRet,m_sRet,pRow); while (m_bRet) { int iId,iBaseType; System::String^ fciname,^fciremark; m_sql->ProcessRow2(pRow,m_result[0],m_result[1],m_result[2],m_result[3]); iId = m_result[0]; iBaseType = m_result[1]; fciname = m_result[2]; fciremark = m_result[3]; Console::WriteLine( " select * from TCI_TYPEBASEINFO is:{0},{1},{2},{3}", iId,iBaseType,fciname,fciremark); m_bRet = m_sql->FetchRow(m_iRet,m_sRet,pRow); } m_sRet = Console::ReadLine(); // 6. 用SELECT2,FETCHROW,PROCESSROW来遍历多条记录 m_bRet = m_sql->Select2(m_iRet,m_sRet, " select FCITYPEID,FCIBASETYPE,FCINAME,FCIREMARK from TCI_TYPEBASEINFO ", pRow,m_result[0],m_result[1],m_result[2],m_result[3]); Console::WriteLine("Select2 result:{0},{1}",m_iRet,m_sRet); while (m_bRet) { int iId,iBaseType; System::String^ fciname,^fciremark; iId = m_result[0]; iBaseType = m_result[1]; fciname = m_result[2]; fciremark = m_result[3]; Console::WriteLine(" select * from TCI_TYPEBASEINFO is:{0},{1},{2},{3}",iId,iBaseType,fciname,fciremark); m_bRet = m_sql->FetchRow(m_iRet,m_sRet,pRow); if (m_bRet == false) break; m_sql->ProcessRow2(pRow,m_result[0],m_result[1],m_result[2],m_result[3]); } m_sRet = Console::ReadLine(); // 7. 关闭数据库 m_bRet = m_sql->DisConnectDB(m_iRet,m_sRet); Console::WriteLine("DisConnect result:{0},{1}",m_iRet,m_sRet); m_sRet = Console::ReadLine(); }
// This is the main DLL file. #include "stdafx.h" #include "vcclr.h" #include "msql.h" using namespace System::Runtime::InteropServices; namespace msql{ tsql::tsql() { m_mysql = NULL; m_bIsConnected = false; } tsql::~tsql() { int^ m_iRet; System::String^ m_sRet; if (m_bIsConnected) DisConnectDB(m_iRet,m_sRet); if (m_mysql) mysql_close(m_mysql); m_bIsConnected = false; m_mysql = NULL; } /* -------------------------------------------------------------------------------------- * ConnectDB * 连接MYSQL数据库。 * 参数: * sServer: mysql服务地址 * sUser: 用户名 * sPasswd: 口令 * sDatabase: 数据库名 * iPort: mysql服务器port * iRetCode: 返回值 * sRetMsg: 返回信息 * 返回: true: 连接数据库成功. * false:连接数据库失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * -------------------------------------------------------------------------------------- * 用法: * m_bRet = m_sql->ConnectDB(L"127.0.0.1","root","mypass","test",3306,m_iRet,m_sRet); * -------------------------------------------------------------------------------------- */ bool tsql::ConnectDB(System::String^ sServer, // MYSQL服务器名 System::String^ sUser, // MYSQL用户名 System::String ^ sPasswd, // MYSQL密码 System::String^ sDatabase, // MYSQL数据库 int iPort, // MYSQL端口 int ^% iRetCode, // 返回值 System::String ^% sRetMsg // 返回信息 ) { bool m_bRet; int m_iRet; System::String^ m_sRet; int rc; MYSQL *m_t; m_sRet = L""; m_iRet = 0; m_bRet = true; /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == true) { m_sRet = L"已经连上数据库"; goto L_RET; } if (m_mysql == NULL) { m_mysql = mysql_init(NULL); } m_t = mysql_real_connect(m_mysql, (const char *)(void*)Marshal::StringToHGlobalAnsi(sServer), (const char *)(void*)Marshal::StringToHGlobalAnsi(sUser), (const char *)(void*)Marshal::StringToHGlobalAnsi(sPasswd), (const char *)(void*)Marshal::StringToHGlobalAnsi(sDatabase), iPort,NULL,CLIENT_MULTI_RESULTS); if (m_t == NULL) { m_sRet = gcnew System::String((const char*)(mysql_error(m_mysql))); m_iRet = mysql_errno(m_mysql); m_bRet = false; goto L_RET; } m_bIsConnected = true; L_RET: iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet;; } /* -------------------------------------------------------------------------------------- * DisConnectDB * 断开MYSQL数据库。 * 参数: * iRetCode: 返回值 * sRetMsg: 返回信息 * 返回: true: 断开数据库成功. * false:断开数据库失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * -------------------------------------------------------------------------------------- * 用法: * m_bRet = m_sql->DisConnectDB(m_iRet,m_sRet); * -------------------------------------------------------------------------------------- */ bool tsql::DisConnectDB(int ^% iRetCode, // 返回值 System::String ^% sRetMsg // 返回信息 ) { bool m_bRet; int m_iRet; System::String^ m_sRet; m_sRet = L""; m_iRet = 0; m_bRet = true; /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == false) { m_sRet = L"已经断开数据库"; goto L_RET; } mysql_close(m_mysql); m_mysql = NULL; m_bIsConnected = false; /*---------------------------------------------------------------------------------*/ L_RET: iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet; } /* -------------------------------------------------------------------------------------- * f_StringToChar * System::String转换为char数组 * 参数: * pStr: 被转换的System::String * pOut: 输出的char指针的指针 * piSize: 输出转换后的大小 * 返回: 转换后的大小。 * -------------------------------------------------------------------------------------- * 用法: * 由于转换为char指针需要分配内存,如果由调用者分配,则调用者可能不知道实际所需大小。 * 因此这里采用了两种的兼容方式。如果调用者不分配,则由本函数分配,调用者释放。如果调用者 * 分配,且大小足够,则直接用调用者的指针,如果大小不够,则本函数分配,调用者释放。 * char * pStr = NULL; * int iStr = 0; * f_StringToChar(sSql,&pStr,&iStr); * // dosomething, * f_StringToChar(sSql,&pStr,&iStr); //这里无需考虑pStr和iStr,函数内会自动调整 * // ... * if (pStr) // 最后释放内存 * delete [] pStr; * -------------------------------------------------------------------------------------- */ int tsql::f_StringToChar(System::String ^pStr, // 被转换的System::String char **pOut, // 转换出的char 数组 int * piSize) // 转换出的大小. { pin_ptr<const wchar_t> wch = PtrToStringChars(pStr); // Convert to a char* size_t origsize = wcslen(wch) + 1; const size_t newsize = 100; size_t convertedChars = 0; // char* nstring=new char[newsize]; if (*pOut == NULL) { *pOut = new char [newsize]; if (piSize) *piSize = (int)newsize; } else { if (piSize) { if (*piSize < (int)newsize) { delete [] *pOut; *pOut = new char [newsize]; if (piSize) *piSize =(int) newsize; } } } wcstombs_s(&convertedChars, *pOut, origsize, wch, _TRUNCATE); return newsize; } /* -------------------------------------------------------------------------------------- * Execute * 执行SQL语句。执行完成后,也可以用FETCHROW,PROCESSROW来进行处理结果集。 * 参数: * iRetCode: 返回值 * sRetMsg: 返回信息 * sSql: SQL语句 * 返回: true: 执行成功. * false:执行失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * -------------------------------------------------------------------------------------- * 用法: * m_bRet = m_sql->Execute(m_iRet,m_sRet," set names gbk"); * -------------------------------------------------------------------------------------- */ bool tsql::Execute(int ^% iRetCode, // 输出的错误代码 System::String ^% sRetMsg, // 输出的错误信息 System::String^ sSql) // SQL语句 { bool m_bRet; int m_iRet; System::String^ m_sRet; char *pStr; int iStr; m_sRet = L""; m_iRet = 0; m_bRet = true; pStr = NULL; iStr = 0; f_StringToChar(sSql,&pStr,&iStr); /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == false) { m_sRet = L"已经断开数据库"; m_bRet = false; m_iRet = -1; goto L_RET; } /*---------------------------------------------------------------------------------*/ int rc; rc = mysql_query(m_mysql,pStr); if (rc) { m_iRet = mysql_errno(m_mysql); m_sRet = gcnew System::String(mysql_error(m_mysql)); m_bRet = false; goto L_RET; } /*---------------------------------------------------------------------------------*/ L_RET: if (pStr) delete [] pStr; iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet; } /* -------------------------------------------------------------------------------------- * Select * 执行单行的SELECT语句。 * 参数: * iRetCode: 返回值 * sRetMsg: 返回信息 * sSql: SQL语句 * ...: 输出的变量集合 * 返回: true: 执行成功. * false:执行失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * -------------------------------------------------------------------------------------- * 用法: * array<tResult^>^ m_result; * int i; * m_result = gcnew array<tResult^>(4); * for(i=0;i<4;i++) * m_result[i] = gcnew tResult(); * m_bRet = m_sql->Select(m_iRet,m_sRet," select count(*) from TCI_TYPEBASEINFO ",m_result[0]); * * Select的结果用tResult保存,可以转换为int,double,System::String等类型 * -------------------------------------------------------------------------------------- */ bool tsql::Select(int ^% iRetCode, // 输出的错误代码 System::String^% sRetMsg, // 输出的错误信息 System::String^ sSql, // SQL语句 ... array<tResult ^>^ pOutArray // 输出的变量集合 ) { bool m_bRet; int m_iRet; System::String^ m_sRet; char *pStr; int iStr; m_sRet = L""; m_iRet = 0; m_bRet = true; pStr = NULL; iStr = 0; f_StringToChar(sSql,&pStr,&iStr); /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == false) { m_sRet = L"已经断开数据库"; m_bRet = false; m_iRet = -1; goto L_RET; } /*---------------------------------------------------------------------------------*/ int rc; rc = mysql_query(m_mysql,pStr); if (rc) { m_iRet = mysql_errno(m_mysql); m_sRet = gcnew System::String(mysql_error(m_mysql)); m_bRet = false; goto L_RET; } tRow^ pRow; m_bRet = FetchRow(iRetCode,m_sRet,pRow); if (m_bRet == false) { m_iRet = *iRetCode; goto L_RET; } ProcessRow(pRow,pOutArray); delete pRow; // 显示释放pRow,目的是执行mysql_free_result L_RET: if (pStr) delete [] pStr; iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet; } /* -------------------------------------------------------------------------------------- * Select2 * 执行单行返回的的SELECT语句。 * 参数: * iRetCode: 返回值 * sRetMsg: 返回信息 * pRow: 中间变量,用于记录中间结果集 * sSql: SQL语句 * ...: 输出的变量集合 * 返回: true: 执行成功. * false:执行失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * -------------------------------------------------------------------------------------- * 用法: * array<tResult^>^ m_result; * int i; * tRow^ pRow = nullptr; * * m_result = gcnew array<tResult^>(4); * for(i=0;i<4;i++) * m_result[i] = gcnew tResult(); * m_bRet = m_sql->Select2(m_iRet,m_sRet, * " select FCITYPEID,FCIBASETYPE,FCINAME,FCIREMARK from TCI_TYPEBASEINFO ",pRow, * m_result[0],m_result[1],m_result[2],m_result[3]); * * Select的结果用tResult保存,可以转换为int,double,System::String等类型 * -------------------------------------------------------------------------------------- */ bool tsql::Select2(int ^% iRetCode, // 输出的错误代码 System::String ^% sRetMsg, // 输出的错误信息 System::String^ sSql, // SQL语句 tRow ^% pRow, // 中间结果集变量,初始化为nullptr,使用中可以不管 ... array<tResult ^>^ pOutArray // 输出的变量集合 ) { bool m_bRet; int m_iRet; System::String^ m_sRet; char *pStr; int iStr; m_sRet = L""; m_iRet = 0; m_bRet = true; pStr = NULL; iStr = 0; f_StringToChar(sSql,&pStr,&iStr); /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == false) { m_sRet = L"已经断开数据库"; m_bRet = false; m_iRet = -1; goto L_RET; } /*---------------------------------------------------------------------------------*/ int rc; rc = mysql_query(m_mysql,pStr); if (rc) { m_iRet = mysql_errno(m_mysql); m_sRet = gcnew System::String(mysql_error(m_mysql)); m_bRet = false; goto L_RET; } m_bRet = FetchRow(iRetCode,m_sRet,pRow); if (m_bRet == false) { m_iRet = *iRetCode; goto L_RET; } ProcessRow(pRow,pOutArray); L_RET: if (pStr) delete [] pStr; iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet; } /* -------------------------------------------------------------------------------------- * ProcessRow * 将FetchRow的信息填写到变量。 * 参数: * pRow: 中间变量,用于记录中间结果集 * ...: 输出的变量集合 * 返回: 无 * -------------------------------------------------------------------------------------- * 用法: * ProcessRow(pRow,pOutArray); * m_sql->ProcessRow2(pRow,m_result[0],m_result[1],m_result[2],m_result[3]); * -------------------------------------------------------------------------------------- */ void tsql::ProcessRow(tRow ^ pRow, // 中间结果集变量,初始化为nullptr,使用中可以不管 array<tResult ^>^ pOutArray // 输出的变量集合 ) { ProcessRow(pRow->result,pRow->row,pOutArray); } void tsql::ProcessRow2(tRow ^ pRow // 中间结果集变量,初始化为nullptr,使用中可以不管 ,...array<tResult ^>^ pOutArray // 输出的变量集合 ) { ProcessRow(pRow->result,pRow->row,pOutArray); } void tsql::ProcessRow(MYSQL_RES *result, // result记录 MYSQL_ROW row, // 行记录 array<tResult ^>^ pOutArray // 输出的变量集合 ) { int i; int iNumFields; MYSQL_FIELD *field; int iLen; iNumFields = mysql_num_fields(result); for(i=0;i<iNumFields;i++) { if (i>=pOutArray->Length) break; field = mysql_fetch_field_direct(result,i); if (field == NULL) break; if (field->max_length == 0) iLen = field->length; else iLen = field->max_length; pOutArray[i]->pUnsigned = new unsigned char [iLen]; memcpy(pOutArray[i]->pUnsigned,row[i],iLen); switch(field->type) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_NEWDECIMAL: pOutArray[i]->po = gcnew int(atoi(row[i])); break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: pOutArray[i]->po = gcnew double(atof(row[i])); break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_BIT: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_ENUM: pOutArray[i]->po = gcnew System::String(row[i]); break; case MYSQL_TYPE_NULL: default: pOutArray[i]->po = gcnew System::String(""); break; } } } /* -------------------------------------------------------------------------------------- * FetchRow * 获得一行信息。 * 参数: * iRetCode: 返回值 * sRetMsg: 返回信息 * pRow: 获得的行信息 * 返回: true: 获得行成功. * false:获得行失败,iRetCode,sRetMsg中存放着错误代码和错误信息 * 如果有多个结果集,会自动取得下一个结果集,直到取到行,或者取完。 * -------------------------------------------------------------------------------------- * 用法: * m_bRet = m_sql->FetchRow(m_iRet,m_sRet,pRow); * -------------------------------------------------------------------------------------- */ bool tsql::FetchRow(int ^% iRetCode,System::String ^% sRetMsg,tRow ^% pRow) { bool m_bRet; int m_iRet; System::String^ m_sRet; m_sRet = L""; m_iRet = 0; m_bRet = true; /*---------------------------------------------------------------------------------*/ if (m_bIsConnected == false) { m_sRet = L"已经断开数据库"; m_bRet = false; m_iRet = -1; goto L_RET; } /*---------------------------------------------------------------------------------*/ MYSQL_RES *result; MYSQL_ROW row; MYSQL_FIELD *field; int rc; int iNumFields; int i; while (1) { if (pRow == nullptr) { result = mysql_use_result(m_mysql); // 取结果集 while (result == NULL) // 如果没有结果集,就下一个结果集 { L_NEXTRESULT: rc = mysql_next_result(m_mysql); if (rc) { m_iRet = -1; m_sRet = L"没有结果集返回!"; m_bRet = false; if (pRow != nullptr) { pRow->result = NULL; delete pRow; } pRow = nullptr; goto L_RET; } result = mysql_use_result(m_mysql); } } else result = pRow->result; row = mysql_fetch_row(result); // 取行 if (row == NULL) { mysql_free_result(result); goto L_NEXTRESULT; } break; } if (pRow == nullptr) { pRow = gcnew tRow(result,row); } else { pRow->result = result; pRow->row = row; } /*---------------------------------------------------------------------------------*/ L_RET: iRetCode = m_iRet; sRetMsg = m_sRet; return m_bRet; } };