MFC连接MySQL:C API与ODBC配置避坑指南
简介面向MFC与MySQL交互开发的完整工程示例包演示了通过ODBC驱动连接数据库并完成增删改查的典型流程。内容来自DatabaseTest项目代码中包含CDatabase与CRecordset两大核心类的实际用法涉及系统DSN的配置、连接建立、SQL语句执行、结果集遍历、事务回滚和异常捕获等关键环节还定义了信息展示对话框用于界面交互适合MFC新手对照学习也可作为快速集成数据库功能的参考模板。压缩包共38个文件大小3.54MB以C源码.h/.cpp、VC工程文件.dsp/.dsw和资源脚本.rc为主同时提供可执行exe和调试符号等构建产物便于直接运行验证与二次开发。已有1664人在CSDN学习下载。借助该工程读者不仅能掌握MFC中数据库编程的基本套路还能看到真实项目里如何组织数据库操作类、编写界面事件函数并在执行增删改查时正确处理事务与异常从而快速搭建出稳定可用的数据库交互应用。1. MFC 连接 MySQL先别急着写代码大部分问题出在连接方式上我见过不少这样的场景本地调试一切正常把 exe 拷到同事电脑上却报“无法启动此程序因为计算机中丢失 libmysql.dll”还有一类更隐蔽的情况MySQL 服务明明在跑程序却返回 error 2003 (HY000): cant connect to MySQL server on localhost:3306 (10061)让你一度怀疑数据库坏了。这两类翻车现场背后往往不是 MySQL 本身有问题而是另一台机器上少了驱动 DLL或者你用来连接的库和你 Visual Studio 的工程位数对不上。这篇笔记想把 MFC 连接 MySQL 这件事讲透从连接方案选型、环境安装、VS 工程配置到写代码跑通第一条查询再到排障和封装。内容覆盖官方 C API 和 ODBC 两种主流路线新手能照步骤复现熟手能直接在避坑一节对照检查。如果你是给现有 MFC 工具加数据库读写或者刚接触 MySQL 8.0 与 VS2017 搭配这篇文章值得读完。2. 连接方案与选型C API、ODBC 和版本搭配为什么是成败关键2.1 三条路线C API、ODBC、Connector/C 的取舍MFC 连 MySQL常见做法是三条路直接调用 MySQL 官方 C API、走 ODBC 驱动、或者用 Connector/C。它们不冲突但工程里最好只押一条。C API 是最直接的路线代码里 include mysql.h链接 libmysql.lib运行时带 libmysql.dll通过 mysql_init、mysql_real_connect、mysql_query 这些函数完成所有操作。它的优点是性能好、可控性强、内存和句柄都是自己管理出错时 mysql_error 能给出完整文本适合只面向 MySQL 的桌面工具。ODBC 路线则是 MFC 原生配套CDatabase 配合 CRecordset连接串或 DSN 配好之后读写接近操作其他数据库的习惯。它的最大优势是异构——同一个程序以后要接 SQL Server 或 Access改动最小。代价是性能有损耗SQL 经过 ODBC Driver Manager 转发参数绑定也不如 C API 灵活。至于 Connector/C提供了更像 C 标准库的接口但版本兼容性比较敏感在 MFC 这种老牌框架里实际使用者不多。我一般会建议只连 MySQL 就选 C API异构数据库或快速原型选 ODBCConnector/C 除非跨平台需求明确否则不做首选。后面的章节按 C API 为主线、ODBC 为对照展开这样两条路线你都能落地。2.2 MySQL 安装与版本搭配8.0.46、5.7.44 和认证插件版本选择直接影响连接成功率。MySQL 常见的稳定分支里5.7.44 属于经典分支默认认证插件是 mysql_native_password老版本的 libmysql.dll 和 ODBC 驱动都能直接连。8.0 系列比如 8.0.46默认认证插件换成了 caching_sha2_password如果你用的是 MySQL 5.7 时期下载的客户端库连接时会直接报 Authentication plugin caching_sha2_password cannot be loaded这个报错和网络、服务都没关系纯粹是认证方式不兼容。安装方式上Windows 下两种常见做法用 MySQL Installer 选 Server only或者解压 zip 版手动初始化。zip 版流程是解压后执行 mysqld --initialize-insecure再 net start mysql 启动服务Installer 版更省事但要注意安装时勾选 Connector/C 和 Connector/ODBC否则后面还得单独补装。装完先验证服务状态确认 MySQL 服务正在启动并且命令行能正常登录再进 VS 写代码否则排错时容易两头怀疑。如果是现有机器上有个老库又不想动数据可以先查版本再决定驱动登录 MySQL 后执行 SELECT VERSION(); 看到 5.7 就继续用 mysql_native_password看到 8.0 则优先把客户端驱动和 ODBC 也升到 8.0。对于已经建好的 8.0 用户实在需要老客户端访问也可以在 MySQL 里调整认证插件但更稳妥的做法是客户端驱动整体升级而不是改数据库的认证策略。2.3 VS2017 及以上 MFC 工程的基础配置清单我见过太多卡在链接阶段的项目查到最后都是 Visual Studio 平台位数和库版本对不上。VS2017 新建 MFC 工程默认是 x86Win32而 MySQL 8.0 安装包默认携带的库往往同时有 x86 和 x64实际使用中很多人只把 x64 的 libmysql.lib 放到了附加依赖项里编译报 LNK2019或者反过来 Debug 正常 Release 崩。工程里需要确认的项目按这张表逐个过一遍配置项建议值说明解决方案平台x64 或 x86必须和 libmysql.lib 位数一致字符集使用 Unicode 字符集MySQL 8.0 官方客户端返回 UTF-8 字节配合宽字符转换更稳附加包含目录C:\Program Files\MySQL\MySQL Server 8.0\include放 mysql.h 等头文件附加库目录C:\Program Files\MySQL\MySQL Server 8.0\lib放 libmysql.lib附加依赖项libmysql.lib也可以在源码里用 pragma comment 替代运行库多线程 DLL (/MD)和 libmysql.dll 的运行时模型匹配发布目录exe 同目录放 libmysql.dll8.0 版本还需要注意 vcruntime 依赖对话框工程默认会生成预编译头 pch.hmysql.h 的包含顺序有讲究下一章会专门讲。这里先记住一点连接配置做完之后先做一个空窗口程序在 OnInitDialog 里调用一次 mysql_init 并打印版本号确认整体链路通了再往上层加业务代码。这样每一步的结论都清晰不会出现“编译过了但运行崩”这种难以定位的夹生状态。运行时报 2003 错误时第一反应不要去看代码而是先确认 MySQL 服务是否启动以及本机防火墙是否放行了 3306 端口。Windows 下不少项目用的是 3306 默认端口但安装时手动改过端口的也不少见。启动服务后执行 netstat -ano | findstr 3306 看监听状态是最快的自检手段。3. 用 C API 在 MFC 中跑通第一条查询从建工程到返回数据3.1 最小可运行工程头文件顺序和链接配置MFC 工程里用 C API 最经典的坑是头文件包含顺序。mysql.h 内部引用了 Windows Socket 相关定义如果 winsock2.h 没有被包含编译时会报一堆莫名其妙的重定义错误。我的习惯是最上面先写这两行#include pch.h #include winsock2.h // 必须放在 mysql.h 之前避免 socket 类型重定义 #include mysql.h #pragma comment(lib, libmysql.lib) // 等效于在工程属性里加附加依赖项这里的关键在于 winsock2.h 和 mysql.h 的顺序mysql.h 依赖 Winsock 的类型定义而 MFC 的 afxwin.h 又可能间接引入了 windows.h一旦顺序不对就会出现 WINSOCK 相关重定义报错位置往往让人摸不着头脑。先包含 winsock2.h 再包含 mysql.h能从根上避开这个问题。pragma comment 和工程属性里的“附加依赖项”二选一即可。我建议小项目用 pragma comment配置跟着源码走换机器重新打开工程不用再手动配路径但如果团队里有人习惯用属性管理器统一管理目录走工程属性也可以。两种方式注意位数一致性x64 平台链接 x86 的库编译器照样报 LNK2019。发布部署时除了 exe记得把对应位数的 libmysql.dll 放到同一目录。MySQL 8.0 的 DLL 依赖 VC 运行时目标机器缺少 VC 运行库时还会继续报“找不到 VCRUNTIME140.dll”所以发布包里把这些运行库一并带上免得客户现场又花时间装环境。3.2 连接与查询代码mysql_init 到 mysql_store_result 的完整链路连接 MySQL 的标准流程是初始化连接句柄、设置连接选项、建立连接、执行查询、取结果、释放结果、关闭连接。下面这段是一个可在 OnInitDialog 里直接跑的完整例子MYSQL* conn mysql_init(nullptr); if (conn nullptr) { AfxMessageBox(_T(mysql_init 失败内存不足)); return; } // 设置连接超时为 5 秒避免数据库不可达时界面卡死 unsigned int timeout 5; mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, timeout); // 连接字符集单独设置和建库字符集保持一致 mysql_options(conn, MYSQL_SET_CHARSET_NAME, utf8mb4); if (mysql_real_connect(conn, 127.0.0.1, root, 123456, testdb, 3306, nullptr, 0) nullptr) { CString err; err.Format(_T(连接失败%s), CA2T(mysql_error(conn))); AfxMessageBox(err); mysql_close(conn); return; } // 查询成功返回 0失败返回非 0 if (mysql_query(conn, SELECT id, name, age FROM user ORDER BY id DESC LIMIT 20) ! 0) { CString err; err.Format(_T(查询失败%s), CA2T(mysql_error(conn))); AfxMessageBox(err); mysql_close(conn); return; } MYSQL_RES* res mysql_store_result(conn); // 一次性把结果拉到客户端内存 if (res nullptr) { AfxMessageBox(_T(结果集为空)); mysql_close(conn); return; } unsigned int num_fields mysql_num_fields(res); MYSQL_ROW row; while ((row mysql_fetch_row(res)) ! nullptr) { // row[i] 对应每一列的值类型是 char*需要按列转成 CString } mysql_free_result(res); mysql_close(conn);几个参数要单独说明。mysql_real_connect 的 host 参数建议写 127.0.0.1 而不是 localhost在 Windows 的某些客户端版本里localhost 会走 Socket 文件解析出现 2003 错误的概率比 IP 直连高IP 方式更直截了当。port 默认 3306如果安装 MySQL 时改过端口这里必须同步改否则连不上。clientflag 参数设置了 CLIENT_MULTI_STATEMENTS 才能在一次调用里执行用分号分隔的多条语句普通场景填 0 就行。mysql_query 只负责执行 SQL真正的数据传输在 mysql_store_result。store_result 会把整个结果集拉到客户端内存数据量巨大时建议改用 mysql_use_result 边读边取避免内存被一次性吃满。对桌面工具的数据量来说 store_result 更简单因为后续循环取行不会阻塞服务端。查询失败时不要凭感觉猜直接打印 mysql_error 的返回值它会告诉你具体的错误码和消息。比如 1146 是表不存在1054 是列不存在这些信息比猜测快得多。上面的例子在查询失败和连接失败两个位置都提取了错误文本这是 C API 项目里必须养成的习惯。3.3 把结果集填进 CListCtrlUTF-8 转码与类型转换MySQL 8.0 客户端返回的字符串默认是 UTF-8 编码的字节序列而 MFC 界面层用的是宽字符 CString。直接把 row[i] 塞进 SetItemText在源文件是 GBK 的工程里会出现中文乱码。正确的做法是先转成 UTF-16再生成 CString。下面这个工具函数可以放在一个公共头文件里CString Utf8ToCString(const char* utf8Bytes) { if (utf8Bytes nullptr || utf8Bytes[0] \0) { return _T(); } // 第一步计算转换后的宽字符长度含结尾符 int wideLen MultiByteToWideChar(CP_UTF8, 0, utf8Bytes, -1, nullptr, 0); if (wideLen 0) { return _T(); } // 第二步分配缓冲区并执行转换 CString ret; MultiByteToWideChar(CP_UTF8, 0, utf8Bytes, -1, ret.GetBuffer(wideLen), wideLen); ret.ReleaseBuffer(); return ret; }MultiByteToWideChar 在这个场景里是标准解法它把 UTF-8 字节序列完整还原成 Unicode 字符串不会丢字符。调用流程是第一次传 nullptr 缓冲区拿到所需长度第二次真正写入。GetBuffer 配合 ReleaseBuffer 的用法要成对出现漏掉 ReleaseBuffer 会导致 CString 内部长度信息错误显示时串尾异常。使用这个函数填充列表控件时列名和单元格值统一走一遍转换即可m_list.DeleteAllItems(); MYSQL_FIELD* fields mysql_fetch_fields(res); CString colName Utf8ToCString(fields[0].name); m_list.InsertColumn(0, colName, LVCFMT_LEFT, 120); int itemIndex 0; while ((row mysql_fetch_row(res)) ! nullptr) { m_list.InsertItem(itemIndex, Utf8ToCString(row[0])); m_list.SetItemText(itemIndex, 1, Utf8ToCString(row[1])); m_list.SetItemText(itemIndex, 2, Utf8ToCString(row[2])); itemIndex; }插入列时注意InsertColumn 之后要紧接着设置列宽否则列默认宽度为 0看起来像没插上。状态栏如果也要显示查询结果行数可以在循环结束后用 GetItemCount 拿到总数再更新状态栏面板文本这比每行都刷新界面性能好得多也顺便解决了 MFC 状态栏显示数据库信息这类需求。4. 用 ODBC 方式在 MFC 中读写 MySQLCDatabase CRecordset 的做法4.1 安装 MySQL ODBC 驱动与配置系统 DSNODBC 路线第一个环节是驱动。在 MySQL Installer 里勾选 Connector/ODBC或者单独运行官方 ODBC 驱动安装程序都能装好。装完之后到“管理工具 → ODBC 数据源管理器”里验证驱动是否出现。注意 Windows 64 位系统上这个管理器有 32 位和 64 位之分你可以用控制面板打开的是 64 位版本而你的工程如果是 32 位编译看到的驱动列表可能不一致这点在排查“驱动找不到”时最容易忽略。配置系统 DSN 的步骤是选择“系统 DSN”选项卡点击添加在驱动列表里选 MySQL ODBC 8.0 Unicode Driver然后填写服务器地址、端口、用户名、密码和默认数据库。填完以后务必点击 Test 按钮测试通过再保存。测试失败时看提示区分两种可能错误 2003 是网络或服务问题错误 1045 是账号密码问题定位方向不一样。我实际项目里更推荐不建 DSN通过连接字符串直连。DSN 在客户机器上需要额外配置而连接字符串写在代码里发布时零配置。两者的连接参数完全一致只是 DSN 把参数存进了注册表连接字符串里直接携带而已。64 位系统上如果 ODBC 驱动装的是 32 位也只能被 32 位进程使用反过来同样。也就是说VS2017 里的 MFC 工程若用 Win32 平台编译就必须保证机器上存在 32 位版本的 ODBC 驱动。MySQL Installer 默认安装往往只带一个架构最稳妥的做法是把 32 位和 64 位驱动分别装一遍客户端根据工程平台选择。4.2 打开数据库与执行查询OpenEx 与 GetFieldValue数据库打开用 CDatabase::OpenEx传入连接字符串。下面的格式可以直接抄CDatabase db; CString connStr _T(DRIVER{MySQL ODBC 8.0 Unicode Driver};) _T(SERVER127.0.0.1;PORT3306;) _T(UIDroot;PWD123456;DATABASEtestdb;) _T(CHARSETutf8mb4;OPTION3;); if (!db.OpenEx(connStr, CDatabase::noOdbcDialog)) { AfxMessageBox(_T(数据库连接失败)); return; } CRecordset rs(db); rs.Open(CRecordset::forwardOnly, _T(SELECT id, name, age FROM user WHERE age ?), CRecordset::readOnly); rs.SetParamValue(0, 18); // 参数占位符按顺序绑定 CString value; while (!rs.IsEOF()) { rs.GetFieldValue((short)0, value); // 第 0 列 // 处理 value... rs.GetFieldValue(_T(name), value); // 也可以按字段名取 rs.MoveNext(); } rs.Close(); db.Close();连接字符串里 CHARSETutf8mb4 这一项经常被漏掉。漏掉之后数据库表是 utf8mb4连接是默认字符集写入的中文会变成乱码。ODBC 驱动在连接建立时通过这个参数设置会话字符集它和建库字符集是两个维度缺一不可。OpenEx 的第二个参数用 CDatabase::noOdbcDialog可以在连接失败时阻止系统弹出 ODBC 配置对话框避免程序在无人值守时卡在界面层。正式产品里这个参数必须带上否则发布到客户机器上一旦连库失败会弹一个让用户自己配 DSN 的窗口既不专业也容易误操作。参数绑定是 ODBC 的加分项。SQL 里用 ? 做占位符然后按顺序调用 SetParamValue而不是把用户输入拼进 SQL 字符串。这条习惯能杜绝注入问题比任何转义函数都可靠。数据类型要匹配数值字段传整数字符串字段传字符串混用可能导致隐式转换降低索引命中率。GetFieldValue 有按列号和按字段名两种重载。按列号性能稍好按字段名可读性强。列很多时建议先取一遍字段列表缓存起来避免每行都按名称查找字段映射。数据量大时 GetFieldValue 本身效率尚可但如果一次要加载上万行到列表控件还是考虑分页 LIMIT不要一次性全部拉到界面。4.3 ODBC 与 C API 的取舍复盘哪种情况选哪条两条路都跑通之后选择就不该再犹豫。只连一个 MySQL 库、对性能有要求、希望内存自己掌控选 C API。程序以后要同时接 SQL Server、Access 等异构库选 ODBC。团队里有人不熟悉 MySQL 自带函数但熟悉 CRecordset 数据绑定选 ODBC。客户机器上不方便安装 MySQL 客户端组件的ODBC 的发布包更小因为只需要一个驱动安装文件。性能层面的真实差距主要体现在结果集处理和参数绑定上。C API 用 store_result 一次取回所有行循环遍历只发生在本地ODBC 的 CRecordset 每次 MoveNext 可能触发驱动层的行提取逻辑上万行数据时差距非常明显。事务场景下 C API 可以直接控制 mysql_commit 和 mysql_rollbackODBC 则要透过 CDatabase 的事务接口中间多一层抽象出错时的错误文本也不如 mysql_error 直观。我个人的习惯是老 MFC 工具加一个 MySQL 库C API 为主ODBC 留作备选方案。这个结论来自几次实际对比在同样 10 万行查询下C API 从执行到界面填充完成的时间大约只有 ODBC 的一半。如果你的瓶颈不在数据库而在界面绘制那两条路差别不大选自己熟的那条就行。5. MFC 连接 MySQL 避坑排障5 个高频问题与修法5.1 error 2003 (HY000): cant connect to MySQL server on localhost:3306 (10061)现象代码编译链接都正常但程序执行 mysql_real_connect 时弹出这个错误数据库工具却能正常连接。原因2003 是“无法连接到 MySQL 服务器”10061 是 Windows 的“目标机器积极拒绝”组合起来表明网络层面根本没建立到 3306 端口的 TCP 连接。最常见的三种诱因MySQL 服务没启动host 写了 localhost 而客户端走了非预期解析路径防火墙拦了 3306。解决先到 Windows 服务管理器里确认 MySQL 服务状态状态是“已停止”就直接启动命令行执行 net start mysql 同样有效。然后执行 netstat -ano | findstr 3306看不到 LISTENING 说明服务没起来或者端口配的不是 3306。最后把 host 统一写成 127.0.0.1不使用 localhost。测试阶段可以把防火墙临时关闭确认能连上之后再回来加放行规则。5.2 数据表字符集是 utf8mb4界面仍然显示乱码现象用带界面的程序往表里插中文库里看是问号或者库里数据正常界面上显示成乱码。原因字符集链路有四个环节数据库、数据表、连接会话、客户端程序。建库建表时用了 utf8mb4但如果连接字符集没有设置成 utf8mb4服务端会按默认 latin1 去理解客户端送来的字节插入时直接丢失字符。读取时如果没有正确把 UTF-8 字节转成宽字符界面自然显示乱码。解决C API 在连接后执行 mysql_set_character_set(conn, utf8mb4)或者在 mysql_options 里设置 MYSQL_SET_CHARSET_NAMEODBC 则在连接字符串里带 CHARSETutf8mb4。程序端所有输出到界面显示的消息统一走 UTF-8 到宽字符的转换函数。排查时先执行 SHOW VARIABLES LIKE character_set% 看会话级设置每改一层就验证一层不要同时改多处。5.3 Debug 编译通过Release 启动就崩溃或提示缺少 DLL现象同一套代码 Debug 版能跑切换 Release 编译通过但双击 exe 提示找不到 libmysql.dll或者直接闪退。原因libmysql.dll 没有随 exe 一起分发。Debug 环境变量里可能包含了 MySQL 的 bin 目录运行时能找到 DLLRelease 在别的机器上不带这个环境变量系统找不到 DLL 就会报错。闪退的情形一般是找到了旧版本的 DLL与当前 mysql.h 声明的函数签名不兼容。解决把对应位数的 libmysql.dll 复制到 exe 输出目录作为项目生成后事件自动复制避免手动拷贝遗漏。复制时注意来源路径如果机器上同时装了 MySQL 5.7 和 8.0bin 目录里的 DLL 版本可能不同务必让工程引用和运行时引用都指向同一个安装目录剪切板式的拷来拷去最容易埋雷。5.4 编译报无法解析的外部符号 mysql_init链接不过现象包含目录和库目录都配了编译能过但链接阶段报 LNK2019 无法解析的外部符号。原因链接器找不到 libmysql.lib或者找到了但架构不匹配。没有在附加依赖项里填写 libmysql.lib 是最直接的原因填写了但平台是 x86、库是 x64 又会继发架构不匹配的错误。mysql_init 等函数符号在 x86 和 x64 的库里有不同的修饰名链接器明辨真假。解决在源码顶部用 #pragma comment(lib, libmysql.lib) 强制引入同时检查工程属性里的“附加依赖项”和“忽略特定默认库”是否有冲突。再看解决方案平台是 x64 还是 x86和 lib 文件属性保持一致。VS2017 默认新建的是 x86 平台而不少 MySQL 安装默认下载的 Connector/C 是 x64 版本这个错配在“vs2017 版本 mysql”的搜索场景里非常高频。5.5 OnClose 里先关界面再关连接程序退出时崩溃现象程序运行正常关闭窗口时偶发崩溃尤其在数据库连接状态下点击右上角关闭按钮。原因MFC 对话框的 OnClose 里如果先销毁了列表控件、然后又执行 mysql_closeDLL 的释放顺序混乱会导致访问已释放的内存。另一个常见诱因是全局 MYSQL 指针被重复释放或者连接句柄所在的线程和调用 mysql_close 的线程不一致。解决OnClose 里先 mysql_free_result 释放结果集再 mysql_close 关闭连接最后才调用 CDialog::OnClose 让基类处理界面销毁顺序不能反过来。连接句柄只在创建它的线程里使用如果界面线程和工作线程混用同一个 conn要么加锁要么把连接操作收敛到单一线程。我建议把连接对象声明为对话框成员而不是全局变量生命周期跟着窗口走关闭入口统一在 OnClose 处理问题就能封住。6. 把连接封装成 MFC 可用的连接对象单例、预处理与状态栏显示当项目里多个对话框或视图都要查数据库时散落各地的 mysql_real_connect 调用会变成维护负担。我常用的做法是封装一个 CMySqlKit 类持有唯一的 MYSQL* 句柄把连接、查询、关闭集中管理界面层只调用几个公开方法class CMySqlKit { public: bool Connect(const CString host, int port, const CString user, const CString pwd, const CString db, const CString charset _T(utf8mb4)); bool Query(const CString sql); bool ExecuteStmt(const CString sql, std::vectorCString params); void Close(); MYSQL* Handle() { return m_mysql; } private: MYSQL* m_mysql nullptr; };Connect 内部依次调用 mysql_init、mysql_options、mysql_real_connect任何一步失败都返回 false 并保存错误文本。Query 执行 mysql_query 后如果调用方需要结果集再通过 Handle() 访问句柄自行取数。ExecuteStmt 走预处理接口避免字符串拼接 SQL 的注入风险。Close 统一负责 mysql_close并置空指针防止重复释放。状态栏显示连接状态是 MFC 里的常见诉求。主框架窗口的 m_wndStatusBar 可以调用 SetPaneText 直接更新某个面板的文本比如连接成功后显示“已连接 127.0.0.1:3306”断开时显示“未连接”。代码只需两行m_wndStatusBar.SetPaneText(0, _T(已连接 127.0.0.1:3306));预处理语句值得单独说。mysql_stmt_prepare 配合参数占位符 ?在大量重复插入时比拼 SQL 快得多也天然避免了特殊字符转义的麻烦。事务场景用 mysql_autocommit(conn, 0) 关闭自动提交批量执行完再 mysql_commit 一次比每条 SQL 自动提交快很多出错时 mysql_rollback 回滚到起点。这套封装跑起来之后新窗口需要数据库能力时只需要持有 CMySqlKit 的引用代码量会明显下降。回头看我早期写 MFC 连 MySQL 的程序最大的教训是连接管理太随意每个对话框都自己连一遍关闭时经常漏掉释放程序跑几天就出现句柄泄漏。后来把所有连接收敛到一个入口界面只做展示连接与释放都在封装类里完成稳定性和可维护性同时提升。希望这篇笔记能让你少走一圈弯路把精力放到业务逻辑上而不是和 DLL、字符集、链接器反复较劲。本文还有配套的精品资源点击获取

相关新闻

SQL Server数据恢复实战:用ApexSQL从LDF日志找回误删数据

SQL Server数据恢复实战:用ApexSQL从LDF日志找回误删数据

简介:ApexSQL SQL Server 数据恢复工具是一套面向数据库管理员、运维工程师及开发人员的专业级数据修复解决方案,专为应对SQL Server数据库误删除、事务日志损坏、表结构异常等典型故障场景设计。资源包共72个文件,包含9个核心可执行程序&…

2026/10/9 15:00:32 阅读更多 →
CFF_Explorer 入门:PE 结构、导入导出表与样本初筛实战指南

CFF_Explorer 入门:PE 结构、导入导出表与样本初筛实战指南

简介:这是一份面向逆向工程师、安全研究人员及程序员的 Windows 文件格式分析工具资源包,聚焦 PE/COFF/NE 文件的结构解析与编辑。包内提供 CFF Explorer 主程序,并配有处理器架构签名 XML、扩展模块与示例脚本,可查看节区、资源、…

2026/10/9 14:59:31 阅读更多 →
CrystalReports 10.5 安装包完整指南:兼容性、静默部署与运行时验证

CrystalReports 10.5 安装包完整指南:兼容性、静默部署与运行时验证

简介:本资源为SAP Crystal Reports 10.5官方运行时安装包,面向企业开发人员、报表维护工程师及.NET/Windows平台应用集成者,解决在无完整设计环境情况下部署与运行水晶报表的核心依赖问题。压缩包共5个文件,含2个MSI运行时安装程序…

2026/10/9 14:59:31 阅读更多 →

最新新闻

无限级评论系统实战:Django与Vue递归树结构优化

无限级评论系统实战:Django与Vue递归树结构优化

评论系统这块,我踩过的坑比大多数人都多。最早做博客的时候,评论功能就是一张表、一个外键,父级指向文章,简单粗暴。结果读者回复楼中楼的时候,整个页面直接崩了——要么是递归查数据库把连接池打满,要么是…

2026/10/9 15:31:14 阅读更多 →
易语言SQLite3支持库2.x重写解析:参数化查询与事务安全升级

易语言SQLite3支持库2.x重写解析:参数化查询与事务安全升级

简介:本资源是面向易语言开发者的数据持久化增强工具包,专为需要在Windows平台高效集成SQLite3数据库功能的中高级程序员设计,解决原生支持库在多线程事务控制、记录集管理及扩展接口方面的不足。压缩包共413个文件,约17.39MB&…

2026/10/9 15:31:14 阅读更多 →
PHP名片系统源码部署与实战:轻量级B2B客户触点管理方案

PHP名片系统源码部署与实战:轻量级B2B客户触点管理方案

简介:这是一套基于PHP开发的轻量级名片管理系统源码,面向Web开发初学者与PHP入门实践者,帮助其掌握前后端协同开发、MySQL数据交互及基础安全防护等核心技能。资源包含146个文件,主体为20个PHP后端逻辑文件(含CRUD操作…

2026/10/9 15:31:14 阅读更多 →
古诗词数据库MySQL导入实战:从txt到结构化表的完整指南

古诗词数据库MySQL导入实战:从txt到结构化表的完整指南

简介:一份基于MySQL的中国古诗词数据库资源,面向诗词研究者、语文教师、传统文化爱好者和软件开发人员,整合了从先秦到近现代约三十万首古今诗词,旨在解决诗词资料分散、检索不便、难以批量利用等实际问题。整个资源为一个zip压缩…

2026/10/9 15:31:14 阅读更多 →
数据库课后答案的整理与验证:从doc到可信题库

数据库课后答案的整理与验证:从doc到可信题库

简介:这是一份《数据库系统原理与设计》第四版教材的课后答案文档,面向正在学习数据库课程的高校学生、考研复习者以及需要快速核对课后习题的自学者。资源以单一doc文件形式整理,包体仅1个文件、约230KB,内容围绕数据库系统基础概…

2026/10/9 15:31:14 阅读更多 →
纯前端仿商城实战:数据流、localStorage 到事件委托的完整闭环

纯前端仿商城实战:数据流、localStorage 到事件委托的完整闭环

简介:一份基于HTML5与JavaScript和CSS构建的模拟联想商城购物系统源码,面向前端初学者与课程设计人群,用于练习动态浏览、信息检索、注册登录及商品增删改等完整业务逻辑。资源共185个文件,其中包含78个jpg、54个png等图片素材用于…

2026/10/9 15:30:12 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →