如果你学过 C++,又刚把 Qt6 装上,打算写一个带数据库的项目练手,那么 Qt6 + MySQL 这个小项目应该排在前面。它不像纯 C++ 控制台练习那样只跟算法打交道,也不像只做静态界面那样缺少工程感。做完这个项目,你会理解 C++ 在图形界面程序里怎么组织代码,Qt6 的信号槽机制怎么和界面交互,MySQL 数据又是如何被读出来、写进去的。整条链路跑通之后,后面再做后端工具、桌面客户端、内部管理系统,思路会顺很多。
我建议把这个项目当成“第一个真正完整的小项目”来对待,而不仅仅是在官方案例里跑一遍。因为这类项目最值得看的不是界面有多漂亮,而是“界面 -> 输入 -> 数据库 -> 回显”这条数据流能不能在你的机器上稳定转起来。下面按实际落地的顺序拆开讲。
1. 这个 Qt6 + MySQL 入门项目到底能让你练到什么
1.1 不是“玩具”,而是一条完整的数据流
很多新手练 Qt 时只做计算器、记事本、画板,这类项目虽然能熟悉控件和布局,但缺少真实项目里最关键的“数据持久化”环节。数据一关闭程序就丢了,做完之后仍然不知道一个桌面应用怎么和数据库协作。
Qt6 + MySQL 项目不一样。哪怕是一个最简单的“学生信息管理”或“图书管理”,都会涉及数据库连接、数据表设计、查询结果展示、新增、修改、删除。这些内容正好覆盖了桌面应用开发里最常见的场景:通过界面读取用户输入,组装成数据请求,交给数据库执行,再把执行结果显示回界面。
整个项目做完,你会明白几个一直很模糊的问题:
- Qt6 里连接数据库不是写原生 MySQL C API,而是通过
QSqlDatabase和QSqlQuery; - 界面控件和数据库数据之间需要一个中间处理过程,不能直接画个表格然后自动变成数据库;
- MySQL 服务端的字符集、连接驱动、端口、用户名密码任何一个环节不对,程序打开数据库都会失败。
这比单纯背 SQL 语句更有价值,因为你会看到 SQL 是怎么在一个有界面的程序里被真正调用的。
1.2 适合人群和前置要求
这个项目主要适合已经掌握 C++ 基础语法的人。不需要你精通模板、智能指针、Lambda 表达式,但至少要能看懂class、函数、成员变量、循环、判断,能编译运行一个简单 C++ 程序。
对 Qt 的要求很宽松。你只需要知道 Qt Widgets 里窗口、按钮、输入框、表格这些控件大概是怎么回事,不需要提前背信号槽规则。因为在这个项目里,你会通过实际连接ui控件来理解信号槽,比看书更直观。
对 MySQL 的要求也不高。你不需要会写复杂的存储过程、触发器,也不需要对服务端配置有多深的理解。只要能在本机启动 MySQL 服务,能用命令行或 Workbench 建一个数据库和一张表,后面的事情就可以放到 Qt 程序里完成。
如果你完全没装过 MySQL,也不熟悉命令行,建议先花半天时间把 MySQL 装好,确认能用mysql -u root -p登录进去,再开始 Qt 项目。不要试图一边装 MySQL 一边写连接代码,那样会分不清问题出在数据库还是出在 Qt。
1.3 项目选型的理由:为什么用 C++/Qt6/MySQL
这个组合不是唯一选择,但在学习路径上确实比较稳。
C++ 是一门要求你对数据类型、内存和编译过程有基本控制力的语言。Qt6 在这之上提供了成熟的 GUI 框架,如果只用标准库写窗口程序,工作量会非常大。MySQL 则是使用率很高、资料很多的关系型数据库,和 Qt 之间的连接属于常规操作,网上踩坑经验也多。
相比 Python + PyQt5,Qt6 + C++ 的编译过程更复杂,新手一开始会被 CMake、编译器、依赖库绕晕,但这也正好是真实 C++ 项目绕不开的部分。早一点接触这些东西,后面看其他人的 Qt 项目就不会太慌。
相比只写 Qt 不使用数据库,这个项目最大的意义是把你从“控件摆放”拉进“业务数据处理”。“控件摆放”只是界面,数据库读写才是很多桌面软件的核心。
2. 开发环境准备:先把 Qt6、MySQL、编译器对齐
2.1 软件版本组合建议
我在本地常用的组合是 Qt 6.5 / MinGW 11.2 / MySQL 8.0,这个组合在 Windows 上比较常见,网上资料也最多。如果你的机器配置不高,也可以使用 Qt 6.2 LTS 版本,稳定性不错,编译器要求也没有那么新。
在开始之前,先明确三件事:
- Qt6 本身不提供数据库驱动,需要安装时勾选 Qt SQL 相关组件,或者至少保证 Qt 安装目录下有
sqldrivers插件; - MySQL 服务端和客户端库是两个东西。连接 MySQL 时,程序除了需要 Qt 的
QMYSQL插件,通常还需要 MySQL 的客户端动态库; - 编译器需要与 Qt 版本匹配。Qt 官方安装包里会区分 MinGW 和 MSVC 两种工具链,选错了会出现“无法解析的符号”“找不到编译器”这类问题。
下面这张表可以帮你快速判断开发环境是否对齐:
| 软件 | 常见选择 | 注意事项 |
|---|---|---|
| Qt6 | 6.2 LTS / 6.5 / 6.6 | 安装时勾选 Qt SQL 和对应编译器的组件 |
| 编译器 | MinGW 11.2+ / MSVC 2019+ | 必须与 Qt 安装包里的 ABI 对应 |
| CMake | 3.16+ | Qt6 默认推荐 CMake 构建 |
| MySQL | 8.x | 安装后确认服务已启动,端口默认 3306 |
| 开发 IDE | Qt Creator / VS Code | Qt Creator 对新手更省心 |
2.2 安装 Qt6 时需要勾选的组件
Qt 官方在线安装器默认会下载很多组件,如果你硬盘空间有限,没必要全部安装。新手第一次做 Qt6 + MySQL 项目,至少要勾选这些内容:
- Qt 6.x 版本条目下的
MinGW 64-bit或MSVC 64-bit编译器组件; Qt Debug Symbols可以不勾,影响不大;Qt SQL相关模块一般在默认项里,但最好在安装时确认;Additional Libraries里的Qt Charts之类的可以先不装,等需要了再更新。
这里有个很容易忽略的点:Qt 安装目录下的plugins/sqldrivers文件夹里,必须能看到qsqlmysql.dll(Windows 下)或对应的libqsqlmysql.so(Linux 下)。如果没有这个插件文件,你的 Qt 程序无论如何连接 MySQL 都会提示QMYSQL driver not loaded。
如果你安装时没看到这个文件,可以先回到安装器里添加组件,把当时没选的 SQL 相关包补上,比手动下载 DLL 省事。
2.3 安装和配置 MySQL
MySQL 安装过程在不同系统上区别不大。Windows 上推荐使用 MySQL Installer,Linux 上可以使用 apt 或 dnf 安装mysql-server,macOS 上可以用 Homebrew 安装。安装完成后,先用命令行验证一下:
mysql -u root -p如果能进入 MySQL 提示符,说明服务端已经正常。如果提示Can't connect to MySQL server,先检查服务有没有启动,端口是不是 3306,防火墙有没有拦截本机连接。
进入数据库后,我喜欢先建一个测试账号,而不是长期使用 root。这里可以新建一个专门给 Qt 程序使用的用户:
CREATE USER 'qt_user'@'localhost' IDENTIFIED BY 'qt_password'; GRANT ALL PRIVILEGES ON qt_demo.* TO 'qt_user'@'localhost'; FLUSH PRIVILEGES;这样做的原因是:如果后续连接程序出现问题,你能很快判断是不是权限配置不对,而不是把所有锅都甩给 Qt 代码。同时,也避免项目代码里长期暴露 root 密码,养成好习惯。
如果你用的是 MySQL 8.0,还要留意认证插件。MySQL 8.0 默认使用caching_sha2_password,旧版 Qt 或旧版连接库可能不支持。这个问题我在后面排查部分会再提。
2.4 验证开发环境
环境配置完成后,不要一上来就打开 Qt Creator 新建项目,先花几分钟确认各软件都能独立工作。
- Qt Creator 能正常打开并新建一个空窗口项目;
- MySQL 命令行能成功执行建库、建表、查询语句;
- 系统环境变量里能看到 MySQL 安装目录和 Qt 的编译器路径。
我在实际测试时发现,很多“连接不上数据库”的问题,最后都出在最基础的地方:MySQL 服务没启动、用户名密码写错、数据库名不存在、端口被占用。这些在写代码之前就越早排除越好。
如果你的电脑上还装了其他数据库服务,或者有多个 MySQL 实例,端口可能不是默认 3306。连接代码里必须使用实际端口,别想当然。
3. 创建第一个 Qt Widgets 项目
3.1 使用 Qt Creator 创建项目
Qt Creator 是目前搭建 Qt 项目最直接的方式。打开后选择New Project,在模板里选Qt Widgets Application,然后按提示填写项目名称和构建系统。
我建议项目名称简单一点,不要带空格和中文,比如QtMysqlDemo。构建系统选择 CMake,因为 Qt6 官方已经全面转向 CMake,qmake虽然还能用,但新的学习资料、第三方项目里 CMake 占多数。
创建完成后,Qt Creator 会生成几个基础文件:
main.cpp:程序入口;mainwindow.h/mainwindow.cpp:主窗口类;CMakeLists.txt:构建配置。
先在这个空项目里跑一次,确认能弹出空白窗口。这一步是为了排除编译器和构建环境的问题。如果空窗口都出不来,先别急着写数据库代码。
3.2 CMake 工程文件的基本结构
在 Qt6 中,CMakeLists.txt 至少要在这个文件里引入 Qt 的 Widgets 模块和 SQL 模块。项目不需要数据库时,可能只依赖 Widgets;有了 MySQL 操作,就必须加入 SQL。
一个典型的 CMakeLists.txt 可以这样写:
cmake_minimum_required(VERSION 3.16) project(QtMysqlDemo VERSION 1.0) find_package(Qt6 REQUIRED COMPONENTS Widgets Sql) qt_standard_project_setup() qt_add_executable(QtMysqlDemo main.cpp mainwindow.cpp mainwindow.h ) target_link_libraries(QtMysqlDemo PRIVATE Qt6::Widgets Qt6::Sql)关键就是find_package和target_link_libraries。很多新手在写代码时包含了QSqlDatabase头文件,却忘记在 CMake 里链接Qt6::Sql,编译时就会报“找不到 QSqlDatabase”。实际上这不是头文件路径问题,而是链接库没有加入。
如果你的项目使用.ui文件设计界面,还需要让 CMake 正确找到 UI 文件。Qt6 的qt_add_executable已经能处理一部分自动化内容,但为了稳妥,小项目可以直接在构造函数里手动创建控件,减少配置文件层面的不确定性。
3.3 界面设计:从需求到控件
第一个项目不要做太复杂。我建议做“学生信息管理”,只需要以下几个控件:
- 两个输入框:学生姓名、成绩;
- 三个按钮:新增、修改、删除;
- 一个表格:显示全部学生记录。
这个需求刚好覆盖增删改查。等能跑通之后,你还可以再加一个“按姓名搜索”的输入框,但第一版没必要。
如果你使用 Qt Designer,可以通过拖拽控件快速完成布局。如果已经打开过 Qt Creator,这种方式很适合新手。注意把表格的列数设置为 3 列,分别是 ID、姓名、成绩。ID 一般由数据库自增,不需要用户输入。
界面设计完成后,在mainwindow.h里声明一个槽函数,比如on_addButton_clicked()、on_deleteButton_clicked(),然后在mainwindow.cpp里实现。这是很传统的写法,直观且容易排查。
3.4 先做一条“不需要数据库的界面”
这里有个非常重要的小技巧:在真正连接数据库之前,先让界面程序能独立工作。
什么意思?就是先给按钮写一个测试响应:点击“新增”后,把输入框里的内容打印到控制台,或者先写进表格的第一行。不连数据库,不做真实的 SQL 操作。这一步能确保信号槽连接、控件读取、界面布局都是正常的。
如果一上来就把按钮接到数据库插入代码,一旦出现问题,你根本分不清是界面读取不到输入内容,还是数据库插入失败,还是连接根本没有建立。先拆分验证,后面会省很多时间。
我一般会按这样的顺序测试:
- 程序启动,窗口正常显示;
- 点击“新增”,控制台能输出输入框内容;
- 点击“删除”,能读取出当前选中行;
- 确认这些没问题后,再写数据库连接和 SQL 操作。
4. 连接 MySQL:核心代码、驱动与路径
4.1 QSqlDatabase 连接的基本流程
Qt 操作 MySQL 一般不需要直接使用 MySQL 的 C API,而是通过QSqlDatabase和QSqlQuery。先把连接代码写到一个函数里,这样界面加载时可以调用一次,程序启动时也就完成了数据库初始化。
下面是一段很基础但完整的连接代码:
#include <QSqlDatabase> #include <QSqlQuery> #include <QSqlError> #include <QDebug> bool openDatabase() { QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("127.0.0.1"); db.setPort(3306); db.setDatabaseName("qt_demo"); db.setUserName("qt_user"); db.setPassword("qt_password"); if (!db.open()) { qDebug() << "连接失败:" << db.lastError().text(); return false; } qDebug() << "连接成功"; return true; }这里第一个参数"QMYSQL"是 Qt 内置的 MySQL 驱动名称。如果你的 MySQL 是 MariaDB,通常也可以用QMYSQL,但在生产环境里要注意版本兼容。
如果只想在某个函数里临时用,addDatabase可以不指定连接名。但如果程序里要同时操作数据库,或者担心连接释放时机,可以给连接起一个名字:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "main_connection");这样在获取的时候也可以指定,避免多个连接共享同一个连接实例。
4.2 必须先处理 QMYSQL 驱动问题
很多新手第一次运行上面的代码,会得到错误:
QSqlDatabase: QMYSQL driver not loaded看到这个提示不要慌,它并不代表你的代码写错了,而是 Qt 在运行时找不到 MySQL 驱动插件,或者驱动插件需要的客户端库缺失。
排查时按这个顺序来:
- 在 Qt 安装目录搜索
qsqlmysql.dll或libqsqlmysql.so; - 如果不存在,回到 Qt 安装器,勾选 Qt SQL 组件并安装;
- 如果存在,再把 MySQL 安装目录下的
libmysql.dll(或 MariaDB 的libmariadb.dll)复制到 Qt 的插件目录或者你的可执行程序目录; - 重新启动程序,再看错误提示。
在 Windows 上,qsqlmysql.dll不是一个独立的 DLL,它还会依赖 MySQL 客户端库。因此只把qsqlmysql.dll复制过去不一定能解决问题。需要把 Qt 插件和 MySQL 客户端库都放在可执行程序能找到的位置。
如果你使用 Qt Creator 直接从构建目录运行程序,Qt 会优先加载 Qt 安装目录下的插件,所以你先把libmysql.dll放到 Qt 的bin目录,或者放到可执行文件旁边,都是常见的做法。
在 Linux 上,通常需要安装libmysqlclient-dev或者libmariadb-dev,并且确认 Qt 插件目录路径在LD_LIBRARY_PATH中。
4.3 连接失败的常见排查顺序
连接数据库最常见的错误可以分成几类:驱动问题、连接参数问题、数据库权限问题、认证插件问题。
我把排查顺序整理成了这张表:
| 错误现象 | 优先排查 |
|---|---|
| QMYSQL driver not loaded | Qt SQL 插件是否安装,MySQL 客户端库是否存在 |
| Access denied for user | 用户名、密码、主机权限是否正确 |
| Unknown database | setDatabaseName里写的库名是否存在 |
| Connection refused | MySQL 服务是否启动,端口是否正确 |
| Authentication plugin cannot be loaded | MySQL 8.0 认证方式是caching_sha2_password,连接库是否支持 |
| Lost connection to MySQL server | 防火墙、超时、MySQL 服务是否被误杀 |
如果你在 MySQL 命令行里明明能用同一个账号密码登录,但 Qt 程序连不上,优先怀疑 Qt 使用的连接库和 MySQL 8.0 的认证方式不兼容。一个临时、安全的做法是创建一个专门给 Qt 使用的用户,并指定认证方式:
CREATE USER 'qt_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'qt_password'; GRANT ALL PRIVILEGES ON qt_demo.* TO 'qt_user'@'localhost'; FLUSH PRIVILEGES;这只影响这个新用户,不会动 root 的认证方式。后续正式开发时,你仍然可以使用更安全的认证方式,只要你的连接库支持。
因为连接问题很容易和数据库安装问题混在一起,我建议把“连接数据库”单独作为一个测试函数,界面代码先不写。程序启动后只做一件事:连接数据库,打印结果。等这条通了,再进入界面开发。
5. 实现一个学生信息管理小程序
5.1 需求拆分和表结构设计
在写 SQL 之前,先明确功能。第一版学生信息管理可以精简成三个功能:
- 查看全部学生;
- 新增学生;
- 删除选中学生。
“修改”功能可以放到第二版,因为新增和删除已经能让你理解QSqlQuery的基本用法。如果你想练得更全面,可以把修改功能一起加上。
数据库表结构很简单:
CREATE DATABASE IF NOT EXISTS qt_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE qt_demo; CREATE TABLE IF NOT EXISTS student ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, score DOUBLE NOT NULL DEFAULT 0 );这里有一个小细节:数据库字符集使用utf8mb4,而不是utf8。原因是utf8mb4能完整支持中文和特殊字符,避免在往数据库里存中文时出现乱码或长度报错。在 Qt 界面里输入中文时,如果数据库表是utf8mb4,配合 MySQL 客户端的字符集设置,乱码概率会低很多。
5.2 查询与展示
当数据库连接成功后,第一步实现“查看全部学生”。最简单的做法是使用QSqlQuery查出数据,然后填充到QStandardItemModel里,再把模型设置给QTableView。
核心代码大致是这样:
void MainWindow::refreshTable() { QSqlQuery query; query.exec("SELECT id, name, score FROM student ORDER BY id"); QStandardItemModel *model = new QStandardItemModel(this); model->setHorizontalHeaderLabels({"ID", "姓名", "成绩"}); while (query.next()) { QList<QStandardItem*> rowItems; rowItems << new QStandardItem(query.value(0).toString()) << new QStandardItem(query.value(1).toString()) << new QStandardItem(query.value(2).toString()); model->appendRow(rowItems); } ui->tableView->setModel(model); }这里的query.value(...)会根据列索引取数据。0是 ID,1是姓名,2是成绩。如果表结构变了,或者 SQL 语句里改变了字段顺序,这里的索引也要跟着变。
这个写法逻辑很直白,适合入门。它的缺点是每次查询都会新建一个QStandardItemModel,如果频繁刷新,会有一些内存分配和释放的开销。不过在数据量不大的桌面项目里,完全够用。生产环境可以考虑使用QSqlTableModel或QSqlQueryModel,它们能更直接地绑定数据库。
我建议新手先手动QSqlQuery+QStandardItemModel,因为你能看到数据是怎么一行一行从数据库查到内存里,再变成表格项的。等理解了数据流,再切换到模型类更顺畅。
5.3 新增和删除
新增的按钮槽函数,核心是获取界面输入,然后执行插入 SQL。这里最需要强调的是:不要用字符串拼接 SQL,而是推荐使用参数化绑定。
不推荐的写法:
QString sql = "INSERT INTO student(name, score) VALUES ('" + ui->nameEdit->text() + "', " + ui->scoreSpin->text() + ")"; query.exec(sql);这种写法一旦输入内容包含单引号,或包含特殊符号,轻则 SQL 执行失败,重则形成 SQL 注入风险。虽然本地学习项目不一定会出大事,但刚入门就养成坏习惯,后面很难改。
推荐的写法:
QSqlQuery query; query.prepare("INSERT INTO student(name, score) VALUES (?, ?)"); query.addBindValue(ui->nameEdit->text()); query.addBindValue(ui->scoreSpin->value()); if (!query.exec()) { qDebug() << "插入失败:" << query.lastError().text(); return; } refreshTable();prepare加addBindValue的好处是 Qt 会帮你处理类型转换和转义。成绩输入框如果选用了QSpinBox或QDoubleSpinBox,取值会直接放进去,不用担心用户输入非数字内容。
删除操作类似于“根据选中行的 ID 执行 DELETE”。难点是拿到当前选中行对应的 ID。一般做法是让表格的第一列存 ID,然后根据currentIndex()获取行号,再取那一行的第一列:
int row = ui->tableView->currentIndex().row(); if (row < 0) { QMessageBox::warning(this, "提示", "请先选择一条记录"); return; } QModelIndex index = ui->tableView->model()->index(row, 0); int id = index.data().toInt(); QSqlQuery query; query.prepare("DELETE FROM student WHERE id = ?"); query.addBindValue(id); if (!query.exec()) { qDebug() << "删除失败:" << query.lastError().text(); return; } refreshTable();这里要注意:如果表格已经按某种方式排序,或者用户点击了表头排序,模型的row可能不等于数据库里的顺序,但用 ID 作为条件删除仍然是对的,因为 ID 是主键。
5.4 更新和参数化查询
修改功能其实和新增很相似,差别在于需要先把用户选中的记录回填到输入框,然后执行UPDATE。这一步的关键是取出当前行的 ID 和原值。
这里可以写一个简单的槽函数on_tableView_clicked,当用户点击表格某一行时,把该行的姓名和成绩自动填到上面的输入框:
void MainWindow::on_tableView_clicked(const QModelIndex &index) { int row = index.row(); ui->nameEdit->setText(ui->tableView->model()->index(row, 1).data().toString()); ui->scoreSpin->setValue(ui->tableView->model()->index(row, 2).data().toDouble()); // 记录当前编辑的 ID m_currentId = ui->tableView->model()->index(row, 0).data().toInt(); }然后在“修改”按钮里:
QSqlQuery query; query.prepare("UPDATE student SET name = ?, score = ? WHERE id = ?"); query.addBindValue(ui->nameEdit->text()); query.addBindValue(ui->scoreSpin->value()); query.addBindValue(m_currentId); if (!query.exec()) { qDebug() << "更新失败:" << query.lastError().text(); return; } refreshTable();这个流程能跑通,说明你已经掌握了 Qt6 里最常用的三类 SQL 操作:查询、插入、更新、删除。后面再做订单管理、库存管理、客户管理,本质上都是同一套思路。
5.5 整体验证流程
项目写完后,不要只点一下“运行”就算完。我建议按下面的验证流程走一遍:
- 启动程序,确认数据库连接成功;
- 点击“新增”,填一个中文姓名和成绩,确认表格里能显示新增记录;
- 重启程序,确认这条记录还在,证明数据确实写进了 MySQL;
- 点击“修改”,把姓名改成另一个值,刷新表格,确认数据变化;
- 删除一条记录,确认表格行数减少;
- 在 MySQL 命令行执行
SELECT * FROM student;,确认数据和界面显示一致。
第 3 步尤其重要。很多程序在运行期间看起来正常,但重启后数据丢失,这通常说明程序连的根本不是同一个数据库,或者插入操作没有提交。Qt 的QSqlDatabase默认行为下,执行成功的 SQL 是立刻提交的,所以如果重启后数据还在,说明链路是通的。如果数据不在了,先看 MySQL 命令行里有没有数据。
6. 从入门项目到工程项目的几个扩展点
6.1 界面层、逻辑层、数据层分离
第一个实战项目可以不分层,一切代码都塞在MainWindow里。这样做的好处是直观,缺点是一旦项目变大,MainWindow 会越来越臃肿。当你的窗口类超过 500 行时,就应该考虑把数据库访问独立出来。
最简单的做法是创建一个DatabaseManager类,负责连接数据库、查询学生、新增学生、删除学生。窗口类只负责调用DatabaseManager的方法,并把结果显示到表格里。这样下次换界面,或者换数据库,影响面会小很多。
你也可以把 SQL 语句集中放在一个常量文件里,避免在界面代码里到处出现 SQL 字符串。这对于调试和后续维护非常有用。
6.2 线程、事务和连接池
目前的示例里所有数据库操作都发生在主界面线程。如果只是插入几条记录,不会有什么问题。但如果要导入大量数据,或者执行很慢的查询,界面就会卡住。这时候需要把数据库操作放到子线程里,或者使用异步机制。
Qt 本身提供QtConcurrent、QThread、QThreadPool等方案。新手不用一开始就上子线程,但应该知道数据库查询不是瞬间完成的,尤其是网络数据库、大数据量排序、批量导入,都会卡界面。
批量写入多条记录时,可以使用事务:
QSqlDatabase db = QSqlDatabase::database(); if (!db.transaction()) { qDebug() << "开启事务失败"; return; } for (const auto &record : records) { QSqlQuery query; // query 默认使用当前连接 query.prepare("INSERT INTO student(name, score) VALUES (?, ?)"); query.addBindValue(record.name); query.addBindValue(record.score); if (!query.exec()) { db.rollback(); return; } } db.commit();事务能保证要么全部写入成功,要么全部回滚,不会出现“写了一半,后面失败”的脏数据。
连接池是更高级的话题。如果程序只是一个桌面客户端,平时就一个连接,其实没必要做连接池。如果以后要把这段代码改造成服务端程序,并且同时处理很多请求,才需要考虑连接池。初学者知道有这个东西就行。
6.3 打包发布前的依赖收集
Qt 程序在开发环境能跑,不等于换一台电脑也能跑。发布的时候,需要把 Qt 的 DLL、插件、以及 MySQL 客户端库一起带上。
Qt 官方有windeployqt工具,可以自动扫描可执行文件依赖,并把 Qt 相关的 DLL 和插件复制过去。但 MySQL 客户端库不一定能被它自动识别,所以你可能还需要手动把libmysql.dll或libmariadb.dll放到发布目录里。
另一个常见问题是数据库驱动插件路径。发布版本里必须包含plugins/sqldrivers目录下的qsqlmysql.dll,并且保证程序能找到这个目录。Qt 默认会从程序路径下的plugins目录寻找插件,所以目录结构要保持一致。
同时不要忘了,MySQL 客户端库还可能依赖其他运行库,比如 Visual C++ Redistributable。如果目标机器没有安装对应版本,程序启动时可能没有任何提示,报错也只在系统事件里。
6.4 再往后可以试什么
这个学生信息管理项目只是起点。完成之后,你可以从下面几个方向继续扩展:
- 增加登录界面:用户名密码存到数据库,校验通过才进入主界面;
- 增加分页查询:当记录数超过几百条时,采用
LIMIT offset, count分页; - 增加搜索功能:按姓名模糊查询,使用
LIKE; - 增加日志:把每次操作记录到日志表,方便排查问题;
- 增加导出功能:把查询结果导出成 CSV 或 Excel 文件;
- 改用
QSqlTableModel:直接用模型绑定数据表,省去手动拼接表格的代码。
这些扩展一点一点加上去之后,这个项目就会从“第一个实战”变成“一份可以写进简历的完整桌面端项目”。关键是不要一次加太多,先保持每个版本能跑,再把新功能叠加进去。
我自己的感受是,Qt6 + MySQL 这个组合真正磨人的地方不在 SQL 语句,而在环境、驱动、路径和参数这些工程细节。你把这几关过了之后,后面写再多业务代码都只是熟练度问题。第一次做项目时,别急着追求高级框架和复杂架构,先把“界面能输入、MySQL 能存住、重启后还在”这条最基础的数据流打通,后面的一切才有意义。