尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Qt 6 + MySQL 图书管理系统实战:从驱动配置到增删改查

Qt 6 + MySQL 图书管理系统实战:从驱动配置到增删改查 很多初学者学完 C 语法和 Qt 基础控件之后都会遇到一个很现实的问题网上资料大多是零散的 Demo真正要把 Qt 和 MySQL 连起来做一个完整项目总是卡在驱动配置、编码格式、SQL 语句这些细节上。本文将带你从零开始基于 Qt 6 框架和 MySQL 数据库完成一个带增删改查功能的图书管理系统。不论你是刚学完 C 的在校学生还是想转行 Qt 开发的职场新人都可以照着这篇文章一步步做出来。在动手之前先明确本文的技术范围使用 Qt 6 Widgets 模块构建桌面界面使用 MySQL 8.x 作为数据存储通过 Qt 提供的 QSqlDatabase、QSqlQuery 完成数据库读写操作。项目功能包含图书信息的添加、查询、修改、删除以及列表展示和关键字搜索。读完本文你会掌握 Qt 连接 MySQL 的完整流程、常见错误排查方法以及桌面项目的基本工程组织方式。1. 背景与核心概念1.1 为什么选择 Qt 6 MySQL 做第一个项目Qt 是一个跨平台的 C 图形界面应用程序开发框架常被用于桌面软件、嵌入式界面和工业控制软件开发。MySQL 是最流行的开源关系型数据库之一在中小型项目中应用非常广泛。把两者结合起来做项目是 C 开发者走向实战的一条经典路线。选择这个组合作为第一个数据库项目有几个现实原因Qt 的 QSql 模块封装了数据库访问的通用接口写好的代码可以比较方便地切换到 SQLite、PostgreSQL 等其他数据库。MySQL 的安装、建库、建表、SQL 语句练习都相对简单网上资料丰富遇到问题容易解决。桌面系统 数据库是很多公司内部工具、实验室管理系统、小型信息管理系统的常见架构。对于刚接触 Qt 的小白来说通过这样一个项目可以一次性掌握界面布局、信号与槽、QTableWidget 表格组件、SQL 语句封装、数据库连接管理这些核心知识点。1.2 Qt 连接数据库的基本架构Qt 通过 QSqlDatabase 类表示一个数据库连接对象通过 QSqlQuery 类执行 SQL 语句并获取结果。为了让 Qt 能访问 MySQL需要加载对应的数据库驱动插件名字一般是QMYSQL。数据库操作的整体流程大致如下使用QSqlDatabase::addDatabase(QMYSQL)注册连接。设置主机名、端口、数据库名、用户名、密码。调用open()打开连接。使用QSqlQuery执行 SQL。处理查询结果或错误信息。程序退出时释放连接。整个过程可以理解为Qt 程序是一个客户端通过 MySQL 协议与数据库服务端通信。你不需要关心底层 TCP 数据包是怎么封装的只需要掌握 QSqlDatabase 和 QSqlQuery 这两个类即可。1.3 你将从项目中掌握什么完成本文的图书管理系统后你将具备以下能力在 Windows 环境下安装并配置 Qt 6 与 MySQL 8。使用 Qt Designer 或纯代码方式创建用户界面。通过 QSqlDatabase 建立 Qt 到 MySQL 的连接。使用 QSqlQuery 执行增、删、改、查 SQL。使用 QTableWidget 把查询结果展示在界面上。掌握中文乱码、驱动加载失败等高频问题的排查方法。下面我们从环境准备开始。2. 环境准备与版本说明版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。整体环境如下项目推荐环境操作系统Windows 10 / Windows 11Qt 版本Qt 6.5 及以上Widgets 模块编译器MinGW 64-bit 或 MSVC 2019/2022MySQLMySQL 8.0 及以上MySQL 图形工具MySQL Workbench 8.0可选构建工具qmake 或 CMake2.1 Qt 6 的安装去 Qt 官网下载 Qt Online Installer安装时需要注册一个 Qt 账号。安装向导中注意勾选以下组件Qt 6.5.x 下的MinGW 11.2.0 64-bit或MSVC 2019 64-bit。Qt 5 Compatibility Module可选某些旧代码会用到。Qt Creator是默认自带的不用额外安装。如果只需要 Widgets 项目可以不勾选体积较大的 Android、iOS 模块。注意 MySQL 驱动在安装时没有额外选项它是 Qt 源码的一部分Windows 下通常需要自己编译或使用已编译好的插件。这里要特别提醒Qt 自带的 MySQL 驱动插件不一定直接可用。有时你在代码里写QSqlDatabase::addDatabase(QMYSQL)后会得到QSqlDatabase: QMYSQL driver not loaded的错误。这个问题在后面的常见问题章节会详细讲解。2.2 MySQL 的安装MySQL 官方提供了 ZIP 免安装版和 MSI 安装版。对于新手推荐使用 MSI 安装版安装过程比较简单。安装时需要注意选择Server only或Full模式。设置 root 账号密码时建议先设置为简单密码例如root或123456本地学习环境可以这样用。选择MySQL Workbench方便查看和操作数据库。记住 MySQL 服务名默认是MySQL80。安装完成后可以通过命令行验证mysql -u root -p输入密码后能进入mysql提示符说明安装成功。2.3 创建项目需要的数据库表在开始写 Qt 代码之前我们先在 MySQL 中创建数据库和图书表。打开 MySQL Workbench连接本地实例执行以下 SQLCREATE DATABASE IF NOT EXISTS bookdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE bookdb; CREATE TABLE IF NOT EXISTS book ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 主键, title VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(50) NOT NULL COMMENT 作者, price DECIMAL(10, 2) NOT NULL COMMENT 价格, stock INT NOT NULL DEFAULT 0 COMMENT 库存, publish_date DATE COMMENT 出版日期 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表;这里使用utf8mb4字符集是为了避免中文乱码问题。utf8mb4是 MySQL 中完整的 UTF-8 编码与 Qt 中常用的 UTF-8 编码对应后续写入中文数据时不容易出错。再插入几条测试数据INSERT INTO book (title, author, price, stock, publish_date) VALUES (C Primer Plus, Stephen Prata, 128.00, 20, 2017-06-01), (Qt 6 C GUI Programming, Packt Publishing, 98.50, 15, 2022-11-01), (MySQL 必知必会, Ben Forta, 45.00, 30, 2009-01-01);查询确认一下SELECT * FROM book;到这里数据库侧的准备就完成了。接下来我们创建 Qt 项目。3. Qt 连接 MySQL 的核心原理3.1 在 .pro 文件中添加 SQL 模块在 Qt Creator 中创建新项目选择Application-Qt Widgets Application项目名称可以取为BookManager。默认生成的BookManager.pro文件需要修改添加sql模块QT core gui sql greaterThan(QT_MAJOR_VERSION, 4): QT widgets CONFIG c17 TARGET BookManager TEMPLATE app SOURCES \ main.cpp \ dbmanager.cpp \ mainwindow.cpp HEADERS \ dbmanager.h \ mainwindow.h如果不添加QT sql代码中#include QtSql会直接编译报错。3.2 数据库连接管理类 DBManager为了不让数据库连接的代码散落在各个窗口类里最佳实践是单独写一个数据库管理类。这样做的好处是连接配置集中管理。窗口类只关心业务和数据展示。以后切换数据库时只需要改一个位置。新建dbmanager.h#ifndef DBMANAGER_H #define DBMANAGER_H #include QString #include QSqlDatabase class DBManager { public: static bool openConnection(const QString host, int port, const QString dbName, const QString user, const QString password); static void closeConnection(); }; #endif // DBMANAGER_H新建dbmanager.cpp#include dbmanager.h #include QSqlDatabase #include QSqlError #include QDebug bool DBManager::openConnection(const QString host, int port, const QString dbName, const QString user, const QString password) { // 获取默认连接对象 QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL); db.setHostName(host); db.setPort(port); db.setDatabaseName(dbName); db.setUserName(user); db.setPassword(password); if (!db.open()) { qDebug() 数据库打开失败: db.lastError().text(); return false; } qDebug() 数据库连接成功; return true; } void DBManager::closeConnection() { QSqlDatabase::database().close(); }这里有几点需要注意QSqlDatabase::addDatabase(QMYSQL)的第一个参数是驱动名。如果写成小写MYSQL在某些环境会失败建议直接写QMYSQL。db.lastError().text()用来获取错误信息排错时非常重要。默认连接名没有指定Qt 会自动命名为qt_sql_default_connection。本项目只有一个连接所以不需要使用addDatabase的第二个参数。3.3 QSqlQuery 的三种常见用法QSqlQuery 是执行 SQL 的主要类这里先介绍最基本的使用方式。第一种执行不带参数的 SQL。QSqlQuery query; if (query.exec(SELECT * FROM book)) { while (query.next()) { int id query.value(0).toInt(); QString title query.value(1).toString(); qDebug() id title; } }第二种使用绑定值的方式执行带参数的 SQL。这种方式能避免字符串拼接导致的语法错误和 SQL 注入风险。QSqlQuery query; query.prepare(INSERT INTO book (title, author, price, stock, publish_date) VALUES (?, ?, ?, ?, ?)); query.addBindValue(title); query.addBindValue(author); query.addBindValue(price); query.addBindValue(stock); query.addBindValue(publishDate); if (query.exec()) { qDebug() 插入成功; }第三种查询结果赋值到表格控件。这个在实战项目里会重点用到。QSqlQuery query; query.exec(SELECT id, title, author, price, stock, publish_date FROM book); while (query.next()) { int row ui-tableWidget-rowCount(); ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(query.value(0).toString())); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(query.value(1).toString())); // ...其他列 }这里推荐使用prepare()加绑定参数的方式。虽然直接拼接字符串看起来更简单但在实际项目中一旦用户输入包含单引号、反斜杠或中文标点SQL 很容易出错。使用参数绑定后Qt 会帮你处理转义问题。4. 完整实战案例图书管理系统现在我们把前面讲到的知识整合起来做一个真正可以运行的图书管理系统。功能清单如下打开程序时自动连接数据库。在主界面表格中显示所有图书。支持按书名关键字模糊查询。支持新增图书。支持修改选中图书。支持删除选中图书。4.1 创建项目结构在 Qt Creator 中创建项目后建议把代码组织如下BookManager/ ├── BookManager.pro ├── dbmanager.h ├── dbmanager.cpp ├── main.cpp ├── mainwindow.h ├── mainwindow.cpp └── mainwindow.ui其中mainwindow.ui是界面设计文件可以在 Qt Designer 中拖拽控件生成。本文为了代码更直观会分别说明 UI 和代码的对应关系。4.2 设计主界面打开 Qt Creator 的mainwindow.ui设计模式添加以下控件一个QLineEdit命名为searchEdit用于输入关键字。一个QPushButton命名为searchBtn文本为“查询”。一个QPushButton命名为addBtn文本为“新增”。一个QPushButton命名为updateBtn文本为“修改”。一个QPushButton命名为deleteBtn文本为“删除”。一个QTableWidget命名为tableWidget列数为 6列标题为ID、书名、作者、价格、库存、出版日期。一个QStatusBarMainWindow 默认自带用于显示状态信息。为了让界面布局更整齐可以用垂直布局把所有控件包起来。表格的列宽可以设置为等宽或按内容自适应。4.3 编写主窗口头文件mainwindow.h内容如下#ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); ~MainWindow(); private slots: void onSearchBtnClicked(); void onAddBtnClicked(); void onUpdateBtnClicked(); void onDeleteBtnClicked(); private: void loadAllBooks(); void loadBooksByKeyword(const QString keyword); int getSelectedBookId(); Ui::MainWindow *ui; }; #endif // MAINWINDOW_HloadAllBooks()和loadBooksByKeyword()是辅助函数负责把查询结果填充到表格中。getSelectedBookId()用于获取当前选中行的 ID。4.4 编写主窗口实现mainwindow.cpp内容较多我们分段讲解。先看构造函数和查询按钮逻辑#include mainwindow.h #include ui_mainwindow.h #include dbmanager.h #include QSqlQuery #include QSqlError #include QDebug #include QMessageBox #include QTableWidgetItem MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); setWindowTitle(图书管理系统); // 设置表格为整行选中、不可编辑 ui-tableWidget-setSelectionBehavior(QAbstractItemView::SelectRows); ui-tableWidget-setSelectionMode(QAbstractItemView::SingleSelection); ui-tableWidget-setEditTriggers(QAbstractItemView::NoEditTriggers); ui-tableWidget-verticalHeader()-setVisible(false); // 连接按钮信号 connect(ui-searchBtn, QPushButton::clicked, this, MainWindow::onSearchBtnClicked); connect(ui-addBtn, QPushButton::clicked, this, MainWindow::onAddBtnClicked); connect(ui-updateBtn, QPushButton::clicked, this, MainWindow::onUpdateBtnClicked); connect(ui-deleteBtn, QPushButton::clicked, this, MainWindow::onDeleteBtnClicked); // 启动时加载所有数据 loadAllBooks(); } MainWindow::~MainWindow() { delete ui; }接着是查询函数void MainWindow::onSearchBtnClicked() { QString keyword ui-searchEdit-text().trimmed(); if (keyword.isEmpty()) { loadAllBooks(); } else { loadBooksByKeyword(keyword); } }这里做了一个简单的空判断当输入为空时加载全部数据输入不为空时按关键字模糊查询。loadAllBooks()和loadBooksByKeyword()实现如下void MainWindow::loadAllBooks() { QSqlQuery query; if (!query.exec(SELECT id, title, author, price, stock, publish_date FROM book ORDER BY id)) { QMessageBox::critical(this, 错误, 查询失败 query.lastError().text()); return; } ui-tableWidget-setRowCount(0); int row 0; while (query.next()) { ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(query.value(0).toString())); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(query.value(1).toString())); ui-tableWidget-setItem(row, 2, new QTableWidgetItem(query.value(2).toString())); ui-tableWidget-setItem(row, 3, new QTableWidgetItem(query.value(3).toString())); ui-tableWidget-setItem(row, 4, new QTableWidgetItem(query.value(4).toString())); ui-tableWidget-setItem(row, 5, new QTableWidgetItem(query.value(5).toString())); row; } ui-statusBar-showMessage(QString(共查询到 %1 条记录).arg(row), 3000); } void MainWindow::loadBooksByKeyword(const QString keyword) { QSqlQuery query; query.prepare(SELECT id, title, author, price, stock, publish_date FROM book WHERE title LIKE ? ORDER BY id); query.addBindValue(% keyword %); if (!query.exec()) { QMessageBox::critical(this, 错误, 查询失败 query.lastError().text()); return; } ui-tableWidget-setRowCount(0); int row 0; while (query.next()) { ui-tableWidget-insertRow(row); ui-tableWidget-setItem(row, 0, new QTableWidgetItem(query.value(0).toString())); ui-tableWidget-setItem(row, 1, new QTableWidgetItem(query.value(1).toString())); ui-tableWidget-setItem(row, 2, new QTableWidgetItem(query.value(2).toString())); ui-tableWidget-setItem(row, 3, new QTableWidgetItem(query.value(3).toString())); ui-tableWidget-setItem(row, 4, new QTableWidgetItem(query.value(4).toString())); ui-tableWidget-setItem(row, 5, new QTableWidgetItem(query.value(5).toString())); row; } ui-statusBar-showMessage(QString(共查询到 %1 条记录).arg(row), 3000); }两个函数结构很相似区别只在于是否带 WHERE 条件。实际项目可以合成一个函数但这里分开写是为了让新手理解起来更直接。getSelectedBookId()函数int MainWindow::getSelectedBookId() { int row ui-tableWidget-currentRow(); if (row 0) { return -1; } QTableWidgetItem *item ui-tableWidget-item(row, 0); if (!item) { return -1; } return item-text().toInt(); }然后是新增图书逻辑。这里使用 Qt 标准对话框QInputDialog逐个输入字段为了减少代码复杂度把书籍信息和 ISBN 之类的高级字段先省略掉。void MainWindow::onAddBtnClicked() { QString title QInputDialog::getText(this, 新增图书, 书名); if (title.isEmpty()) { return; } QString author QInputDialog::getText(this, 新增图书, 作者); QString priceStr QInputDialog::getText(this, 新增图书, 价格); QString stockStr QInputDialog::getText(this, 新增图书, 库存); bool priceOk false; bool stockOk false; double price priceStr.toDouble(priceOk); int stock stockStr.toInt(stockOk); if (!priceOk || !stockOk) { QMessageBox::warning(this, 输入错误, 价格或库存格式不正确); return; } QSqlQuery query; query.prepare(INSERT INTO book (title, author, price, stock, publish_date) VALUES (?, ?, ?, ?, NOW())); query.addBindValue(title); query.addBindValue(author); query.addBindValue(price); query.addBindValue(stock); if (!query.exec()) { QMessageBox::critical(this, 错误, 新增失败 query.lastError().text()); return; } QMessageBox::information(this, 提示, 新增成功); loadAllBooks(); }这里为了演示方便出版日期直接用数据库的NOW()函数填充。真实项目中应该由表单选择日期但初学阶段可以先用简单方式理解整体流程。修改图书的逻辑与新增类似区别在于需要先检查用户是否选中了一行数据void MainWindow::onUpdateBtnClicked() { int id getSelectedBookId(); if (id 0) { QMessageBox::warning(this, 提示, 请先选择要修改的图书); return; } QString title QInputDialog::getText(this, 修改图书, 书名); if (title.isEmpty()) { return; } QString author QInputDialog::getText(this, 修改图书, 作者); QString priceStr QInputDialog::getText(this, 修改图书, 价格); QString stockStr QInputDialog::getText(this, 修改图书, 库存); bool priceOk false; bool stockOk false; double price priceStr.toDouble(priceOk); int stock stockStr.toInt(stockOk); if (!priceOk || !stockOk) { QMessageBox::warning(this, 输入错误, 价格或库存格式不正确); return; } QSqlQuery query; query.prepare(UPDATE book SET title ?, author ?, price ?, stock ? WHERE id ?); query.addBindValue(title); query.addBindValue(author); query.addBindValue(price); query.addBindValue(stock); query.addBindValue(id); if (!query.exec()) { QMessageBox::critical(this, 错误, 修改失败 query.lastError().text()); return; } QMessageBox::information(this, 提示, 修改成功); loadAllBooks(); }删除图书时必须做二次确认防止误操作void MainWindow::onDeleteBtnClicked() { int id getSelectedBookId(); if (id 0) { QMessageBox::warning(this, 提示, 请先选择要删除的图书); return; } QMessageBox::StandardButton result QMessageBox::question( this, 确认删除, 确定要删除选中的图书吗该操作不可恢复。); if (result ! QMessageBox::Yes) { return; } QSqlQuery query; query.prepare(DELETE FROM book WHERE id ?); query.addBindValue(id); if (!query.exec()) { QMessageBox::critical(this, 错误, 删除失败 query.lastError().text()); return; } QMessageBox::information(this, 提示, 删除成功); loadAllBooks(); }4.5 main.cpp 入口代码main.cpp中除了创建窗口还要在启动时先连接数据库#include mainwindow.h #include dbmanager.h #include QApplication #include QMessageBox int main(int argc, char *argv[]) { QApplication a(argc, argv); // 数据库连接配置按你的实际环境修改 bool connected DBManager::openConnection( 127.0.0.1, 3306, bookdb, root, your_password); if (!connected) { QMessageBox::critical(nullptr, 错误, 数据库连接失败程序退出。); return 1; } MainWindow w; w.show(); int ret a.exec(); DBManager::closeConnection(); return ret; }注意把your_password替换为你自己的 MySQL root 密码。用户名和密码不建议直接硬编码在代码中但初学阶段为了快速跑通可以先用这种方式。后续在生产环境中应该通过配置文件或者环境变量管理数据库凭证。4.6 运行与验证点击 Qt Creator 左下角的绿色运行按钮如果一切正常会出现主窗口表格中显示 3 条测试数据。然后可以尝试以下操作在搜索框输入“C”点击查询表格只显示包含“C”的书籍。点击“新增”输入一本新书的信息确认后列表刷新新书出现在末尾。选中新书所在行点击“修改”修改价格列表数据更新。选中新书所在行点击“删除”二次确认后列表刷新。如果以上操作都能完成说明整个项目已经跑通了。5. 常见问题与排查思路在实际开发中新手最容易卡在环境配置阶段。下面把高频问题整理出来方便检索。问题现象常见原因解决思路QSqlDatabase: QMYSQL driver not loadedQt 缺少 MySQL 驱动插件检查sqldrivers目录是否有qsqlmysql.dll确认编译环境与 Qt 架构是否一致连接数据库提示Access denied for userMySQL 用户名密码错误或账号不允许远程连接确认密码或创建允许指定主机访问的账号中文显示乱码数据库表字符集、连接字符集、界面编码不一致统一使用utf8mb4字符集MySQL 连接 URL 可加charsetutf8mb4按钮点击后没有反应信号与槽连接失败或槽函数内部有逻辑错误在槽函数中加qDebug()输出确认是否被调用表格中显示的数字带.0数据库 DECIMAL 类型通过 QVariant 转字符串导致使用QString::number(query.value(3).toDouble(), f, 2)格式化端口连接超时MySQL 服务未启动或端口不是 3306检查服务状态确认端口号编译报错找不到QtSql.pro 文件没有添加QT sql在 .pro 文件中添加 SQL 模块程序启动后界面卡死大量数据一次性加载到表格数据量大时使用 QTableView 模型或者分页加载MySQL 8 认证插件冲突旧版客户端驱动不支持caching_sha2_password更新 Qt 驱动或为账号改用mysql_native_password5.1 QMYSQL driver not loaded 的详细排查这个错误出现频率最高单独列出来讲。首先在 Qt 中确认驱动是否可用#include QSqlDatabase #include QDebug qDebug() QSqlDatabase::drivers();如果输出里没有QMYSQL说明驱动插件缺失。常见位置是 Qt 安装目录下的plugins/sqldrivers。MinGW 版本的 Qt 目录结构类似C:\Qt\6.5.0\mingw_64\plugins\sqldrivers这个目录下应该有qsqlmysql.dll。如果没有说明你的 Qt 安装包没有附带 MySQL 驱动。解决办法有两个一是直接下载或从 Qt 源码编译qsqlmysql.dll。编译步骤相对繁琐需要先安装 MySQL 的 C 连接库然后在 Qt 源码中手动编译QMYSQL插件。二是更换思路使用 ODBC 方式连接 MySQL。在 Qt 中通过QODBC驱动配合 Windows 的 MySQL ODBC 驱动实现连接。这样可以绕开QMYSQL驱动缺失的问题适合学习和演示但生产环境还是建议尽量解决原生驱动问题。对于新手我建议优先尝试方法一。网上有很多现成的 qsqlmysql.dll 下载但一定要选择和你 Qt 版本、编译器位数匹配的文件。这里不推荐随便从不明来源下载 DLL最好在自己机器上编译避免安全风险。5.2 中文乱码问题的排查中文乱码通常发生在三个层面数据库表本身不是utf8mb4字符集。MySQL 连接层字符集不一致。Qt 字符串与数据库字符集不匹配。解决方案建表时指定字符集CREATE TABLE book (...) ENGINEInnoDB DEFAULT CHARSETutf8mb4;连接时如果使用 QSqlDatabase可以在设置参数后执行QSqlQuery query(db); query.exec(SET NAMES utf8mb4);Qt 代码源文件统一使用 UTF-8 编码建议在.pro文件中添加msvc { QMAKE_CXXFLAGS /utf-8 }这样 MSVC 编译器会把源代码按 UTF-8 理解避免字符串常量出现乱码。5.3 编译驱动时的注意事项如果你决定自己编译 qsqlmysql.dll需要准备MySQL 安装目录中的include和lib文件夹。Qt 对应版本源码。与 Qt 相同编译器。在 Qt 命令行环境例如Qt 6.5.0 (MinGW 11.2.0 64-bit)中进入 Qt 源码的qtbase/src/plugins/sqldrivers/mysql目录执行qmake和make命令。具体命令不同环境略有差异需要参考文档。编译成功后会生成qsqlmysql.dll把它复制到 Qt 的plugins/sqldrivers目录。这个过程中比较容易出现找不到 mysql.h 文件的问题需要调整 include 路径。6. 最佳实践与工程建议学完上面这个项目后很多人会直接开始写自己的系统。这里想提前分享几条工程实践建议帮你少走弯路。6.1 数据库访问层与界面层分离本文已经把 DBManager 单独抽取出来了但 MainWindow 里仍然直接使用了 QSqlQuery。对于第一个项目来说这样是可以接受的因为业务逻辑简单。但当你写更复杂的项目时建议把 SQL 操作封装到独立的 DAOData Access Object类中例如 BookDao负责所有 book 表的 SQL 操作。主界面只调用这些接口不直接执行 SQL。这种方式的好处是如果将来把数据库从 MySQL 换成 PostgreSQL界面代码几乎不用动只需要改 DAO 层。6.2 不要使用字符串拼接 SQL强烈建议所有 SQL 都使用prepare()加参数绑定。有两个原因避免中文、单引号、特殊字符导致 SQL 语法错误。防止 SQL 注入攻击。例如下面这种写法非常危险QString sql SELECT * FROM book WHERE title title ;如果 title 中包含单引号SQL 就会出错如果用户恶意输入 OR 11则会产生不可预估的后果。正确的做法是query.prepare(SELECT * FROM book WHERE title ?); query.addBindValue(title);6.3 数据库凭证不要硬编码教材里为了方便往往把数据库密码直接写在代码中。但实际项目中这样做有几个问题代码仓库一旦泄露数据库就暴露了。多人协作时不同开发环境密码不同硬编码会造成冲突。部署到生产环境时无法快速切换配置。推荐做法有使用QSettings读取本地配置文件。使用环境变量。使用专门的配置中心公司项目中常见。初学阶段不用做得太复杂但至少应该把配置集中放在一个地方例如config.ini文件。6.4 表格刷新时注意性能本文的loadAllBooks()使用逐行插入QTableWidgetItem的方式。当数据量在几百条以内时问题不大但数据量上万时界面会明显卡顿。优化方式有两种一是先调用ui-tableWidget-setRowCount(0)清空表格再一次性setRowCount(total)设置总行数最后批量设置数据。这样可以避免频繁插入行导致的大量内存操作。二是使用QSqlQueryModel配合QTableView。这种方式更符合模型视图架构代码量更少QSqlQueryModel *model new QSqlQueryModel(this); model-setQuery(SELECT id, title, author, price, stock, publish_date FROM book); model-setHeaderData(0, Qt::Horizontal, ID); ui-tableView-setModel(model);对于第一个项目可以先使用 QTableWidget理解界面和数据的交互关系之后再学习模型视图架构。6.5 增加日志与错误追踪在开发和维护阶段日志是非常有用的工具。不要在用户界面上打一长串 qDebug() 就不管了。推荐把关键操作记录到日志文件例如数据库连接成功/失败。用户执行了新增、删除操作。SQL 执行失败时的错误信息。程序启动和退出时间。Qt 中可以使用自定义日志函数或者使用 QLoggingCategory 来分类记录。日志文件应该放到独立的目录并定期清理。6.6 注意 MySQL 安全配置在本地学习时使用 root 账号没问题但部署到服务器上时一定要遵循最小权限原则为应用创建独立的数据库账号只赋予它操作本项目数据库的权限。不要使用 root 连接业务数据库。数据库端口不要暴露到公网。定期备份数据库。这些内容虽然不是 Qt 代码层面的东西但是一个合格开发者在项目上线前必须考虑的。7. 总结与下一步学习建议到这里你已经完成了一个基于 Qt 6 和 MySQL 的图书管理系统走过了建库、建表、连接数据库、增删改查、界面展示、错误排查的完整流程。从这个项目出发你可以获得几条很直接的进阶路径。第一继续完善该项目。比如增加图书分类、借阅记录、读者管理、图片封面上传等功能。每加一个功能你都会遇到新的数据库设计和 Qt 界面交互问题这都是宝贵的实战经验。第二学习模型视图编程。QTableWidget 适合数据量较小的场景而 QTableView model 是 Qt 数据展示的核心架构。掌握之后你可以用更少的代码处理更大规模的数据。第三深入学习 MySQL。项目里用到了最基本的 SELECT、INSERT、UPDATE、DELETE但真实业务还会涉及联表查询、索引优化、事务控制、存储过程等内容。建议多练习 SQL 语句理解数据库索引的工作原理。第四了解 Qt 的部署发布流程。开发完成后需要把程序打包发布给其他机器使用Qt 提供了 windeployqt 工具可以自动收集所需的 DLL 和插件。发布阶段还会遇到 MySQL 驱动插件缺失的问题到时候你会发现把驱动依赖彻底搞清楚有多重要。最后给你一个实用的小建议不要停留在抄代码试着把图书列表做成双击行即可编辑把查询条件扩展成多字段组合查询给密码框用上 QLineEdit 的密码模式。每当你把一个看似“简单”的需求做出来你对 Qt 和 MySQL 的理解就会加深一层。如果本文对你有帮助可以收藏备用遇到问题随时翻一翻也欢迎在评论区留言交流你在 Qt 连接 MySQL 时遇到的坑一起讨论解决思路。
返回列表