简介:面向需要在 Visual Studio 2013 中集成 MySQL 数据库开发的 C++ 程序员,这份资源给出了 Connector/C++ 在 Win32 与 x64 两种目标平台下的完整编译成果与配套示例。压缩包共 63 个文件、约 24.98MB,核心包含 21 个头文件、20 个静态/导入库文件、9 个 DLL 运行库、3 个 PDB 调试符号,以及 2 个 C++ 示例源文件;其中 mysqlcppconn 与 libmysql 等库文件分为 release/debug 和 32/64 位版本,可直接按需选用。示例工程 MysqlTest 展示了使用 sql::mysql::MySQL_Driver 创建连接、执行 SQL、遍历 ResultSet 并释放资源的常规流程,配合 ReadMe 和工程配置,能够帮助初学或迁移至 VS2013 的开发者快速验证依赖配置、规避编译链接陷阱。已有 512 人学习使用,适合需要离线编译产物或想通过现成代码理解 Connector/C++ 调用方式的读者。
1. 为什么非要在 VS2013 里从源码编译 Mysql Connector C/C++:win32 与 x64 两套库才是关键
关键的坑在预编译包。老项目把 Visual Studio 2013 当工具链写死了,从官网下载的 Connector/C++ 二进制库,要么 MSVC 运行时版本对不上,要么只给 x64 缺 win32,真正链接进工程的时候接二连三报错。把 MySQL Connector C/C++ 的源码在 VS2013 下从头编译一遍,生成 win32 和 win64 两套库文件,所有依赖就都锁在自己的构建环境里,换机器、换目录都不会再出幺蛾子。这份资源就是完整的编译流程和配套示例代码,适合负责老产线维护、需要 C++ 程序连接 MySQL 的工程师,以及还在用 VS2013 做课程设计、临时要用 MySQL 的学生。
2. 编译前的依赖准备:MSVC 兼容、Boost 版本与目标平台选型
写这种编译文章,最先要克服的执念是:官方有预编译包为什么还要自己编?答案是运行时兼容的老账。
2.1 为什么要自己编译,而不是用官方预编译包
VS2013 对应的编译器版本是 MSVC 12.0,它的标准库实现基本停在 C++11 早期状态。MySQL 官方提供的 Connector/C++ 预编译二进制,从 8.0 开始主要面向 MSVC 2015/2017 构建,直接把我这种老环境排除。退一步说,即便用旧版 1.1.x 的官方二进制,也经常遇到 LNK2038 RuntimeLibrary 不匹配——对方用/MD编出来的库,工程里用的是/MT,链接器直接拒绝干活。
判断一个预编译包能不能用,我一般只看一个数字:VS2013 里_MSC_VER是 1800,下载页面标注的构建工具如果是 14.x 或 19.x(分别对应 VS2015/2017),就不用再浪费时间试了,链接阶段必然给脸色。这也是为什么自己编译才是这条路上的正解:源码在手,构建工具链完全由 CMake 加 VS2013 控制,输出的库和消费它的项目处在同一个运行时环境里。
另一个隐藏原因是 API 的双轨制。Connector/C++ 有 Legacy API 和 X DevAPI 两条线,8.x 的预编译包头文件大量使用新标准特性,VS2013 的 STL 消化不了。与其绕开这些,不如挑一个与老编译器匹配的源码版本,编译期和运行期都省心。
2.2 依赖版本配对:CMake、Boost 与 MySQL 客户端库
Connector/C++ 源码不是拿到手就能编,它依赖三个外部组件:CMake 负责生成 VS 工程,Boost 提供锁与智能指针的内部实现,MySQL 客户端库负责底层网络协议。下表是我在实际环境里反复试出来的稳定组合:
| 依赖 | 版本建议 | 说明 |
|---|---|---|
| CMake | 3.4 ~ 3.8 | 3.10 之后对 VS2013 generator 兼容性明显下降 |
| Boost | 1.58 或 1.59 | 过高版本的头文件写法 VS2013 不支持 |
| MySQL 客户端库 | 5.7 或 8.0 对应 libmysql.dll | 8.0 需注意认证插件兼容 |
| Connector/C++ 源码 | 1.1.13 或 8.0.14 早期版 | 1.1.x 编译最稳 |
先说 CMake。VS2013 的 generator 名是Visual Studio 12 2013,CMake 3.10 之后虽然还保留这个 generator,但缓存处理逻辑变了,偶尔会漏生成某些配置文件。我推荐固定 3.8,既能识别-A Win32参数,又不妥协。
Boost 是坑最多的地方。连接器源码里的驱动实现依赖 Boost 的作用域锁和 shared_ptr,CMake 变量WITH_BOOST必须指向包含boost/子目录的根路径。1.58/1.59 是 VS2013 编译窗口的黄金版本,再新的 Boost 里一些头文件开始用新语法,老编译器直接报错。
MySQL 客户端库要留意服务器版本。用 5.7 的 libmysql.dll 连 8.0 服务器时,默认认证插件 caching_sha2_password 会导致连接失败,这是 MySQL 8.0 的默认行为,需要把服务器用户改回 mysql_native_password 才能兼容旧客户端。这属于环境问题,不是代码问题,排查时最容易绕远路。
2.3 win32 还是 x64:位数一致性才是硬道理
库的位数必须与最终可执行程序一致。x86 的 exe 只能用 win32 的 mysqlcppconn.dll 和 libmysql.dll,x64 的 exe 反之。这不是玄学,是 Windows 加载器的工作机制:exe 启动时按自身位数寻找对应架构的 DLL,匹配不上就直接报“不是有效的 Win32 应用程序”。
很多做二次开发的公司,交付物是 x86 的 ActiveX 控件,就必须用 win32 库;而有些上位机系统或数据处理服务是 x64 的,就得配 x64 库。最稳妥的做法是编译前先定平台,后面 CMake 生成和 VS 编译都按这个平台执行,别编了两轮才发现位数不一致。
另外,32 位代码里常见的/arch:SSE2选项在 x64 下没有意义,两个平台的工程配置应该分开管理,不要共用一份 vcxproj 属性表。轻度不一致会在链接阶段报错,重度不一致会在运行时闪退,后者更难查。
3. 动手编译:CMake 生成 VS2013 工程与双平台构建
准备就绪后开始编译。资源包里附带的 build 脚本就是把下面这些 CMake 参数封装成一次调用,你手动执行一遍会更清楚每一步在做什么。
3.1 CMake 参数逐个说明:Generator、Boost、SSL 和 MySQL 目录
假定源码解压到C:\work\mysql-connector-cpp-src,Boost 解压到C:\libs\boost_1_59_0,MySQL 客户端装在C:\Program Files\MySQL\MySQL Server 5.7。先为 win32 构建建一个独立目录:
cd C:/work/mysql-connector-cpp-src mkdir build-win32 cd build-win32 cmake -G "Visual Studio 12 2013" -A Win32 \ -DWITH_BOOST=C:/libs/boost_1_59_0 \ -DWITH_SSL=disabled \ -DMYSQL_DIR="C:/Program Files/MySQL/MySQL Server 5.7" \ -DBUILD_STATIC=OFF \ -DBUILD_TESTING=OFF ..-G "Visual Studio 12 2013"是 CMake 识别 VS2013 的标准名称,“12”是 VS 内部版本号,“2013”是日历版本,两个都不能写错。-A Win32指定目标平台,64 位构建时改成-A x64。WITH_BOOST指向 boost 源码根路径,CMake 会自行找到boost/version.hpp。WITH_SSL=disabled先关掉 OpenSSL 依赖,避免引入第三方编译链,业务需要加密连接时再单独开。MYSQL_DIR是客户端库安装根目录,CMake 在里面查找 libmysql.lib 和 mysql.h。BUILD_STATIC=OFF决定输出动态库,需要静态库的改成 ON。最后的..指向源码根路径。
如果在 Windows cmd 里执行,把行尾的\换成^就能写多行命令。CMake 版本低于 3.1 时不识别-A参数,改用-G "Visual Studio 12 2013 Win64"也能生成 x64 工程,这是老版本 CMake 的写法。生成完成后 build-win32 目录下会出现MYSQLCPPCONN.sln,这就是 VS2013 的解决方案文件。
注意:这里建议把编译目录和源码目录分开,不要直接在源码根目录跑 cmake。混在一起后再想生成另一平台工程,清理起来非常痛苦,我在这上面浪费过半小时。
3.2 在 VS2013 里选择目标与配置:静态库还是动态库
打开解决方案,在“解决方案资源管理器”里能看到多个项目。关键目标有三个:mysqlcppconn(动态库)、mysqlcppconn-static(静态库)、mysqlcppconn_shared(动态库别名)。我一般只编动态库,因为静态库会把依赖代码膨胀进业务 exe,后续调试符号对不上反而麻烦。
在“生成”菜单里打开配置管理器,活动解决方案配置设为 Release,活动解决方案平台设为 Win32。如果你习惯命令行,也可以直接用 devenv:
"C:/Program Files (x86)/Microsoft Visual Studio 12.0/Common7/IDE/devenv.com" \ MYSQLCPPCONN.sln /Build "Release|Win32"编译过程大概 5 到 10 分钟,中间会有若干 Boost 模板的警告,只要不出现 error 就是正常的。结束后到build-win32\driver\Release目录找 mysqlcppconn.dll 和 mysqlcppconn.lib。注意 Debug 和 Release 不能混:用 Debug 版库链接 Release 版 exe,链接阶段报 LNK2038;反过来也一样,库的运行时配置与宿主工程必须统一。
3.3 示例项目连接库配置:附加包含目录与附加依赖项
头文件和库文件都拿到了,开始配使用方工程。在 VS2013 里新建一个 Win32 控制台应用,打开项目属性,依次配置:
C/C++ → 附加包含目录,添加两行:C:\work\mysql-connector-cpp-src\include和C:\work\mysql-connector-cpp-src\include\cppconn。第一行是给mysql_driver.h用的,第二行是给cppconn/*.h这些派生头文件用的,缺一个就会报找不到头文件。
链接器 → 附加库目录,添加编译产物目录:C:\work\mysql-connector-cpp-src\build-win32\driver\Release。链接器 → 附加依赖项,添加mysqlcppconn.lib和libmysql.lib。前者是连接器自身的导入库,后者是 MySQL 客户端协议的导入库,两个缺一不可。
最后把mysqlcppconn.dll和libmysql.dll复制到 exe 的输出目录,与 exe 同级。漏掉这一步,程序启动会直接弹“无法启动此程序,因为计算机中丢失 libmysql.dll”。DLL 的位数再强调一次:Win32 工程放 32 位 DLL,x64 工程放 64 位 DLL。拿反了,Windows 加载器会提示“不是有效的 Win32 应用程序”,处理过很多次这个问题的人才知道它不是病毒也不是缺文件,就是位数对不上。
注意:改了项目属性后先正常编译一次,确认生成了输出目录,再把 DLL 放进去。顺序反了的话,复制 DLL 时找不到目标目录,又得翻一次车。
4. 示例代码拆解:连接、建表、查询与预编译语句
库配好了,跑一个能实际连接的示例。因为资源包里附带了一套完整示例代码,我直接按它的主线拆开讲,你拿去改起来会更顺。
4.1 最小连接代码骨架:驱动单例、连接字符串与资源释放
在 VS2013 的示例项目里新建 main.cpp,先写最小连接逻辑:
#include <iostream> #include <memory> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/exception.h> int main() { try { sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); std::unique_ptr<sql::Connection> conn( driver->connect("tcp://127.0.0.1:3306", "root", "123456")); conn->setSchema("test_db"); std::cout << "connected, server version=" << conn->getServerVersion() << std::endl; } catch (sql::SQLException& e) { std::cerr << "SQL error: " << e.what() << ", code=" << e.getErrorCode() << ", state=" << e.getSQLState() << std::endl; return 1; } return 0; }get_mysql_driver_instance()返回的是库内部管理的单例指针,不需要也不能 delete。driver->connect的第一个参数必须是tcp://IP:端口的完整写法,漏了tcp://前缀,驱动会把整串当成本地 socket 文件路径,连接必然失败。unique_ptr<sql::Connection>保证任何路径下连接都会析构,这在后续使用预编译语句时尤其重要,因为连接不释放,服务端会话就一直在。
异常处理这块,what()是错误描述,getErrorCode()是 MySQL 错误码,getSQLState()是 SQLSTATE 字符串。三个值一起打出来才能快速区分是网络问题、权限问题还是 SQL 语法问题。只看what()的话,像“Access denied”和“Unknown database”容易混淆。
4.2 建表与 DML:Statement 和 executeUpdate 的实际用法
连接成功后,用 Statement 执行不返回结果集的 SQL:
#include <cppconn/statement.h> std::unique_ptr<sql::Statement> stmt(conn->createStatement()); stmt->execute("CREATE TABLE IF NOT EXISTS t_user (" "id INT AUTO_INCREMENT PRIMARY KEY," "name VARCHAR(64) NOT NULL," "age INT)");createStatement()创建语句句柄,和连接对象相互独立,用unique_ptr持有即可。execute适合 DDL;DML 用executeUpdate,它返回受影响行数:
int affected = stmt->executeUpdate( "INSERT INTO t_user(name, age) VALUES('lisi', 30)"); std::cout << "affected rows: " << affected << std::endl;executeUpdate返回的是实际被修改的行数,0 不代表失败,很可能是 WHERE 条件没有命中数据。判断语句是否成功要看有没有抛异常,而不是看行数的大小。这一点和很多从 JDBC 转过来的习惯不一样,容易误判。
查询用executeQuery,返回ResultSet。注意游标只支持向前遍历,next()返回 false 表示没有下一行。getInt、getString的参数可以写列名也可以写列索引,索引从 1 开始:
std::unique_ptr<sql::ResultSet> res( stmt->executeQuery("SELECT id, name, age FROM t_user")); while (res->next()) { std::cout << res->getInt("id") << "\t" << res->getString("name") << "\t" << res->getInt("age") << std::endl; }4.3 PreparedStatement 参数化:防注入与批量执行的性能点
带用户输入的场景必须走预编译语句。老 C++ 工程最爱拼接 SQL 字符串,省事但既慢又有注入风险。Connector/C++ 的 PreparedStatement 写法如下:
#include <cppconn/prepared_statement.h> std::unique_ptr<sql::PreparedStatement> pstmt( conn->prepareStatement("INSERT INTO t_user(name, age) VALUES(?, ?)")); pstmt->setString(1, "wangwu"); pstmt->setInt(2, 28); int inserted = pstmt->executeUpdate(); std::cout << "inserted: " << inserted << std::endl;参数下标从 1 开始,与 ODBC 的习惯一致。setString对应 VARCHAR/CHAR,setInt对应 INT。如果表字段是 BIGINT,务必用setInt64,否则服务端会因类型不匹配截断数值。setDateTime处理 DATETIME,直接传"YYYY-MM-DD HH:MM:SS"格式的字符串即可。
预编译语句 prepare 之后可以反复执行,循环里改参数然后executeUpdate,服务端只解析一次 SQL 模板,批量 INSERT 时性能优势非常明显。注意循环里不要重新prepareStatement,也不要新建 Statement,那样就退化回普通 SQL 了。
5. 避坑:VS2013 编译 Connector/C++ 时最常翻车的五个问题
编译和首次连接是最容易栽跟头的阶段。下面五个问题我基本都踩过,每条按现象、原因、解决写清楚,你遇到类似的直接对号入座。
5.1 LNK2038 运行时库不匹配:Debug/Release 与 /MT、/MD 的混用
现象:VS2013 链接阶段报错,形如LNK2038: mismatch detected for 'RuntimeLibrary': value 'MD_DynamicRelease' doesn't match value 'MT_StaticRelease' in mysqlcppconn.lib。
原因:mysqlcppconn 库源码以 Release +/MD编译,而使用方工程把“代码生成 → 运行库”设成了“多线程 (/MT)”。两种运行时互不兼容,链接器直接拒绝。这个问题在接手别人留下的老工程时特别常见,属性页里可能还残留着上古配置。
解决:打开项目属性 → C/C++ → 代码生成 → 运行库,Release 设为“多线程 DLL (/MD)”,Debug 设为“多线程调试 DLL (/MDd)”。改完要重新编译整个依赖链,因为 /MT 和 /MD 的切换会触发全量重编,别指望增量编译。
5.2 编 8.x 源码报 C2039:VS2013 的 STL 卡在新特性上
现象:用 Connector/C++ 8.x 源码在 VS2013 编译,报错C2039: 'xxx' is not a member of 'std',例如std::is_trivially_copyable找不到。
原因:8.x 内部使用了 VS2013 STL 尚未实现的新特性。VS2013 对 C++11 的支持有明确边界,一些模板元编程写法在老编译器里直接缺符号。
解决:如果非要用 8.x,第一选择是降到 8.0.14 以前还在 VS2013 能编的版本;第二选择是关掉相关编译宏。我一般直接退回 Connector/C++ 1.1.13,Legacy API 接口完全一样,业务代码几乎不用改,编译体验却天差地别。
5.3 运行时找不到 libmysql.dll:复制了文件程序还是闪退
现象:程序在开发机编译运行一切正常,拷到别的机器后启动弹窗,提示缺少libmysql.dll。
原因:开发机上 MySQL 安装目录在 PATH 里,系统能找到 libmysql.dll;换一台干净的机器,PATH 里没有,而 mysqlcppconn.dll 内部依赖它,启动加载链直接断掉。
解决:发布时将libmysql.dll、mysqlcppconn.dll、msvcp120.dll三个文件放进 exe 同目录。同时确认 DLL 位数与 exe 一致:x64 的 exe 配 32 位 libmysql.dll,加载时会报“不是有效的 Win32 应用”,换机器后很容易出现这种怪现象,检查顺序是位数优先,再查缺失。
5.4 CMake 找不到 Boost:WITH_BOOST 路径看似对,实则少了一层
现象:cmake 配置阶段输出Could NOT find Boost,而路径明明指向了 boost 源码目录。
原因:Connector/C++ 的 CMakeLists 把WITH_BOOST当 BOOST_ROOT 用,要求该目录下直接存在boost/version.hpp。如果路径指到了 boost 的include子目录,或者指到了 boost 源码的父目录,查找就失败。
解决:确保C:\libs\boost_1_59_0\boost\version.hpp存在,WITH_BOOST指向C:\libs\boost_1_59_0。更保险的做法是在 cmake 前设置系统环境变量BOOST_ROOT,两个都指向同一路径,避免不同脚本读取不同变量。
5.5 连 8.0 服务器报认证失败:caching_sha2_password 与旧客户端不兼容
现象:程序连接 MySQL 8.0 提示Access denied for user 'root'@'localhost',但用命令行客户端能正常登录。
原因:MySQL 8.0 默认新建用户使用 caching_sha2_password 认证插件,旧版 libmysql 客户端只认识 mysql_native_password,握手阶段插件协商失败。这跟用户名密码对不对无关,是协议层面的不匹配。
解决:在服务器端把用户认证方式切回旧版:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;ALTER USER同时重置了密码,FLUSH PRIVILEGES刷新授权表。这个操作在测试环境很常见,但不要在共享生产库上乱改,先找 DBA 确认。还有一个排查技巧:命令行能连而程序连不上,先在程序里把用户名密码打出来核对,再检查认证插件,顺序别反。
6. 进阶:双平台产物验证与独立部署习惯
编译完成只是起点,最后这两件事做对了,这份库才算真正落地。
6.1 双平台跑一遍验证脚本
在 VS2013 里把示例项目分别切成 Win32 和 x64 各编译一次,跑在同一台机器上,确认两个 exe 都能连上 127.0.0.1 的 MySQL。验证内容不用多,按这个清单:
- 建表语句是否执行成功
- INSERT 返回行数是否为 1
- SELECT 能读出刚插入的数据
- 停掉 MySQL 服务后,程序能打印出异常而不是闪退
验证通过的标准是:同一个 main.cpp,两套位数下编译零 warning、运行零崩溃。如果你在 x64 下看到一堆整型截断警告,多半是int和size_t混用,顺手把res->getInt换成res->getInt64,并调整对应打印格式。
中文乱码的问题也在这个阶段一起解决。连接建立后执行一次stmt->execute("SET NAMES utf8mb4"),再查中文就不会出现问号。字符集问题看起来是库的锅,实际上是连接会话缺了这条初始化语句。
6.2 独立部署文件清单与一个发布习惯
一份库要交给别的开发组或直接上生产机时,最少需要以下文件:
| 文件 | 说明 |
|---|---|
| mysqlcppconn.dll | 连接器动态库 |
| libmysql.dll | MySQL 客户端网络库 |
| msvcp120.dll | VS2013 的 C++ 运行库 |
| app.exe | 业务程序 |
如果采用 mysqlcppconn-static.lib 静态链接,就不用带 mysqlcppconn.dll,但 libmysql.dll 依然需要,连接器的 TCP 协议栈仍由它提供。msvcp120.dll 在装有 VC++ 运行库的环境里可以不重复放,但放进同目录是最省心的做法,能减少现场机器的环境差异。
我后来把老设备程序的发布脚本改成自动拷贝这三个 DLL 到目标目录,再也没人打电话问“为什么我的机器上跑不起来”。从那以后,每次编译完第一件事是检查输出目录里的 DLL 位数,用 dumpbin 看一眼machine (x86)还是machine (x64),确认与目标 exe 匹配才打包。希望帮到你——这个流程在 VS2013 上是死命令,换了别的编译器版本,记住“运行时一致、位数一致、路径一致”三条,基本不会再翻车。
本文还有配套的精品资源,点击获取