这是一篇非常基础的文章。想必每个刚开始学写程序的人,都很有可能会遇到这样的问题:如何用Visual C++编写一个可以操作(读写数据)MySQL的程序?
由于一直在Linux下工作,我很久没写Windows程序了,但是最近急于写一个操作MySQL的Windows程序来用,于是又重新拿起了VC++,写了一个MFC应用程序,临阵磨枪,不亮也光嘛。
在写这个程序的过程中,遇到的有些问题觉得挺扯蛋的,于是干脆就把整个过程给记下来了,整理成这篇文章,知识共享。
开发环境:Visual Studio 2005,Win 7 32位
文章来源:http://www.codelast.com/
【1】在开发VC++程序时,怎样访问MySQL
方法很多,我只就我了解过的来说一说。
①ADO
为了开发方便,你可以通过ADO来连接MySQL,这也是很多教程提到的方法,写起程序来确实简单,但它有一个问题:你需要先安装MySQL ODBC Driver,并在OS里配置一个数据源,否则程序写好了也是不能连接MySQL的。因此,你在一台计算机上配置好了之后,把你写的程序拷贝到另一台计算机上,又要在OS里重新安装MySQL ODBC Driver并配置数据源,这得有多蛋疼啊?!我觉得这是减轻了编码的工作,却增加了部署的工作,因此,不考虑这种方法,放弃ADO。
不过,这里还是要提一下用ADO访问MySQL的代码有多么简单:
在你的VC++项目中初始化ADO(例如在stdafx.h中):
#import "msado15.dll" no_namespace rename("EOF","adoEOF")
其中,msado15.dll是在Windows系统盘里的一个文件,你可以搜索到。可以把它拷贝到工程项目下,就无需写完整路径了。
然后,在操作数据库之前(一般是在项目的App类中),还要初始化一次:
if (!AfxOleInit()) { AfxMessageBox("初始化失败!"); return FALSE; }
无需其他特殊的设置,接着就可以开始写访问MySQL的程序了,关于读写MySQL的示例代码,这里就不附上了。
没错,就这么简单,你无需在项目中链接到任何MySQL的lib,也无需去找MySQL相关的头文件。ADO确实很适合想要在代码编写上省事的人。
文章来源:http://www.codelast.com/
而下面所说的几种方法都是在开发阶段麻烦,但在部署阶段简单(拷贝相关文件到目标机器上即可用,无需配置数据源)。所以,你可以根据实际情况做出取舍。
②使用MySQL C API
MySQL C API是什么?官方说明如下:
The C API provides low-level access to the MySQL client/server protocol and enables C programs to access database contents.
你可以通过它来访问MySQL。我最终用的就是这种方法。
③使用MySQL C++ Connector
MySQL C++ Connector是什么?官方说明如下:
MySQL Connector/C++ is a MySQL database connector for C++. It lets you develop C++ applications that connect to the MySQL Server.
那么,它与MySQL的C API有什么区别呢?为什么要使用它?官方说明如下:
MySQL Connector/C++ offers the following benefits for C++ users compared to the MySQL C API (MySQL client library):Convenience of pure C++; no C function calls requiredSupports JDBC 4.0, an industry standard APISupports the object-oriented programming paradigmReduces development timeLicensed under the GPL with the FLOSS License ExceptionAvailable under a commercial license upon request
MySQL C++ Connector的下载链接在这里。你可以根据你的平台下载相应的版本,例如“Windows (x86, 32-bit), ZIP Archive”,解压出来会看到 include 和 lib 这两个目录,里面的文件都是我们需要的。
你可以参考MySQL官方的这篇文章。它比较详细地描述了如何创建一个VC++工程并使用MySQL C++ Connector的方法。
文章来源:http://www.codelast.com/
这里不得不提醒你的是,前面已经说了,比起使用MySQL C API,用MySQL C++ Connector来开发程序要做更多的工作,体现在:MySQL C++ Connector依赖于MySQL库、Boost,因此你还要先有MySQL的开发包、Boost库。Boost是个麻烦的东西,因为它很大,编译出lib很耗时间,很耗CPU资源,如果你和我一样,用的是一台老爷机,那么你会感觉到编译出Boost lib的过程异常痛苦。另外,在撮合了MySQL C++ Connector、Boost、MySQL的一个项目中,如果你的项目参数设置不对,会出现各种奇怪的问题,下面我列举一二:
㈠LINK : warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
解决办法是修改VC++项目属性:
项目→编辑项目属性→链接器→输入→“忽略特定库”里填上“MSVCRT.lib”(不含引号)。
㈡msvcprt.lib(MSVCP80.dll) : error LNK2005: "public: __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV01@@Z) 已经在 XXX.obj 中定义
解决办法是修改VC++项目属性:
项目→编辑项目属性→C/C++→代码生成→运行时库→修改为“多线程调试 DLL (/MDd)”或“多线程 DLL (/MD)”
同时,因为此项修改,可能会导致另一个编译错误:
fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]
如果看到这个错误提示,那么就要再次修改项目属性:
项目→编辑项目属性→配置属性→常规→MFC的使用→修改为“在共享 DLL 中使用 MFC”
然后就解决了此问题。
文章来源:http://www.codelast.com/
这可能只是各种编译问题中的一小部分,除非你运气好,没遇到这些问题,或者你愿意花很多时间来搞定它,否则你一定不想在时间紧张的时候看到这些烦人的错误。
【2】头文件,lib的目录结构
综上所述,我使用的是MySQL C API,那么我需要拿到相关的头文件和lib文件。我是先下载到 mysql-installer-community-5.6.11.0.msi 这个MySQL的安装包,然后(无需安装)用Universal Extractor把它解压出来,得到 mysql-5.6.11-win32.msi 文件,再次使用Universal Extractor解压,最终会拿到include和lib两个目录,就是我们所需要的。然后就把它们都放到项目目录下,我是像下面这样组织目录结构的:
├─include │ └─mysql │ └─server │ │ big_endian.h │ │ (后面省略) │ └─mysql │ │ client_authentication.h │ │ (后面省略) │ └─psi │ mysql_file.h │ (后面省略) │ ├─lib │ └─mysql │ └─server │ │ libmysql.dll │ │ libmysql.lib │ │ mysqlclient.lib │ │ │ ├─debug │ │ mysqlclient.lib │ │ │ └─plugin │ adt_null.dll │ auth.dll │ auth_test_plugin.dll │ libdaemon_example.dll │ mypluglib.dll │ qa_auth_client.dll │ qa_auth_interface.dll │ qa_auth_server.dll │ semisync_master.dll │ semisync_slave.dll │ validate_password.dll
其中,include下的是头文件,lib下的是库文件,它们下面的boost、connector、server分别是Boost、MySQL C++ Connector、MySQL的文件。
有了这样的目录结构,VC++项目怎么设置?
文章来源:http://www.codelast.com/
【3】Visual C++项目设置
打开项目属性设置对话框,依次设置如下(无论是对Debug,还是Release,都这样设置):
①“配置属性”→“常规”→“MFC的使用”→“在静态库中使用MFC”(这一项不一定非要这样设置)。
②“配置属性”→“C/C++”→“常规”→“附加包含目录”,把上面提到的include目录添加进去。对应到上面所说的层级结构,它应该是 XXX\include\mysql\server 这样的目录。如下图所示(后面的步骤也类似,不再图示):
文章来源:http://www.codelast.com/
③“配置属性”→“链接器”→“常规”→“附加库目录”,把上面提到的lib目录添加进去。对应到上面所说的层级结构,它应该是 XXX\lib\mysql\server 这样的目录。
④“配置属性”→“链接器”→“输入”→“附加依赖项”,填入“libmysql.lib”(不包含引号)。
文章来源:http://www.codelast.com/
【4】程序编写
我添加了一个类DataAccess,所有的数据库操作都通过这个类完成。这个类大概是长这个样子的(由于是截取下来的代码片段,所以如果有错误导致无法编译,还请见谅):
头文件:
/** * DataAccess.h * * @author Darran Zhang @ codelast.com */ #pragma once #include "stdafx.h" #include <string> #include <winsock.h> #include "mysql.h" class CDataAccess { public: CDataAccess(void); ~CDataAccess(void); private: MYSQL* m_pSQL; MYSQL_RES* m_pSQLResultSet; protected: BOOL m_bIsConnect; // 数据库是否已连接 public: BOOL ConnectDB(const std::string &strHost, const int nPort, const std::string &strDBName, const std::string &strUserName, const std::string &strPassword); BOOL ExecuteQuery(std::string &strSQL); };
其中,如果没有包含 winsock.h 这个文件的话,编译时会报错:
mysql_com.h(XXX) : error C2146: syntax error : missing ';' before identifier 'fd'
m_pSQL 和 m_pSQLResultSet 这两个指针的类型是在MySQL C API中定义的。
ConnectDB()函数用于连接MySQL,ExecuteQuery()函数用于执行一句SQL。
文章来源:http://www.codelast.com/
实现文件:
/** * DataAccess.cpp * * @author Darran Zhang @ codelast.com */ #include "DataAccess.h" using namespace std; CDataAccess::CDataAccess(void) { m_pSQL = NULL; m_pSQLResultSet = NULL; m_bIsConnect = FALSE; } CDataAccess::~CDataAccess(void) { if(m_pSQL) { mysql_close(m_pSQL); } m_pSQL = NULL; m_pSQLResultSet = NULL; } BOOL CDataAccess::ConnectDB(const string &strHost, const int nPort, const string &strDBName, const string &strUserName, const string &strPassword) { if (m_bIsConnect) { MessageBox(AfxGetMainWnd()->m_hWnd, _T("数据库已连接"), _T("提示"), MB_ICONWARNING | MB_OK); return TRUE; } mysql_library_init(0, NULL, NULL); // 初始化MySQL C API库 if( (m_pSQL = mysql_init(NULL)) == NULL) { MessageBox(AfxGetMainWnd()->m_hWnd, _T("初始化MySQL连接失败"), _T("错误"), MB_ICONERROR | MB_OK); return FALSE; } mysql_options(m_pSQL, MYSQL_SET_CHARSET_NAME, "gb2312"); if (NULL == mysql_real_connect(m_pSQL, strHost.c_str(), strUserName.c_str(), strPassword.c_str(), strDBName.c_str(), nPort, NULL, 0)) { MessageBox(AfxGetMainWnd()->m_hWnd, _T("连接MySQL失败"), _T("错误"), MB_ICONERROR | MB_OK); return FALSE; } m_bIsConnect = TRUE; return m_bIsConnect; // 返回连接是否成功的标志 } BOOL CDataAccess::ExecuteQuery(string &strSQL) { if (!m_bIsConnect) { MessageBox(AfxGetMainWnd()->m_hWnd, _T("尚未连接MySQL"), _T("错误"), MB_ICONERROR | MB_OK); return FALSE; } size_t nReturnValue = mysql_real_query(m_pSQL, strSQL.c_str(), strSQL.length()); return (0 == nReturnValue) ? TRUE : FALSE; }
这个类的使用也很简单,构造一个DataAccess类对象来调用其方法即可。
注意:有一句代码 mysql_options(m_pSQL, MYSQL_SET_CHARSET_NAME, "gb2312") 的作用是设置连接的字符集,我记得我测试的时候,使用gbk字符集的话,向数据库中插入的中文是乱码,所以在这里我将它设置为gb2312,经测试就没有问题了。
文章来源:http://www.codelast.com/
上面的代码片段甚至于没有演示如何取出一个SQL查询得到的记录集,因此在这里补充一下。
假设你有一张名为“student”的MySQL表,有两列:ID(主键),name(字符串)。现在要用程序取出该表里的所有记录,可以像下面那样做:
string strSQL = "SELECT * FROM student"; if(mysql_query(m_pSQL, strSQL.c_str())) { MessageBox(AfxGetMainWnd()->m_hWnd, _T("查询数据库失败"), _T("错误"), MB_ICONERROR | MB_OK); return FALSE; } m_pSQLResultSet = mysql_store_result(m_pSQL); MYSQL_ROW pSQLRow = NULL; while (pSQLRow = mysql_fetch_row(m_pSQLResultSet)) { // 循环读出每一行 for (int i = 0; i <= 1; i++) { // 循环读出每一列 string strFieldValue = pSQLRow[i]; if (0 == i) { // 第1列 long nId = atol(strFieldValue.c_str()); } else if (1 == i) { // 第2列 string name = strFieldValue; } } } mysql_free_result(m_pSQLResultSet); // 释放资源 return TRUE;
文章来源:http://www.codelast.com/
上面的代码还不完整,只是演示了怎么读取SQL查询的结果集。但是有了上面的一系列铺垫,你应该可以写出自己的程序来用了。
文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤
转载需注明出处:codelast.com
感谢关注我的微信公众号(微信扫一扫):