告别SQL语句!用Qt的QSqlTableModel在Windows上快速搞定学生信息管理(Qt5.15/6实战)
零SQL实战用Qt的QSqlTableModel构建学生管理系统在桌面应用开发中数据库操作往往是绕不开的一环。但对于许多刚接触Qt的开发者来说SQL语法可能是一道令人望而生畏的门槛。好消息是Qt提供了一个强大的工具——QSqlTableModel它能让我们在不写一行SQL代码的情况下轻松实现数据库的增删改查功能。本文将带你从零开始用QSqlTableModel构建一个完整的学生信息管理系统。1. 环境准备与项目搭建1.1 开发环境配置首先确保你的开发环境已经就绪操作系统Windows 10/11本文示例基于Windows平台Qt版本Qt 5.15或Qt 6.x数据库SQLite无需额外安装Qt已内置支持在Qt Creator中新建项目时选择Qt Widgets Application模板。项目创建完成后需要在.pro文件中添加SQL模块支持QT core gui sql1.2 数据库初始化我们创建一个connection.h头文件来处理数据库连接#ifndef CONNECTION_H #define CONNECTION_H #include QSqlDatabase #include QSqlQuery #include QMessageBox static bool initDatabase() { QSqlDatabase db QSqlDatabase::addDatabase(QSQLITE); db.setDatabaseName(students.db); if (!db.open()) { QMessageBox::critical(nullptr, 错误, 无法连接数据库); return false; } QSqlQuery query; query.exec(CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, gender TEXT, major TEXT)); return true; } #endif // CONNECTION_H在main.cpp中调用初始化函数#include mainwindow.h #include QApplication #include connection.h int main(int argc, char *argv[]) { QApplication a(argc, argv); if (!initDatabase()) { return 1; } MainWindow w; w.show(); return a.exec(); }2. 核心模型配置与界面设计2.1 QSqlTableModel基础配置在MainWindow的构造函数中我们初始化QSqlTableModel并绑定到界面MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); // 初始化模型 model new QSqlTableModel(this); model-setTable(students); model-setEditStrategy(QSqlTableModel::OnManualSubmit); model-select(); // 设置表头显示名称 model-setHeaderData(0, Qt::Horizontal, tr(学号)); model-setHeaderData(1, Qt::Horizontal, tr(姓名)); model-setHeaderData(2, Qt::Horizontal, tr(年龄)); model-setHeaderData(3, Qt::Horizontal, tr(性别)); model-setHeaderData(4, Qt::Horizontal, tr(专业)); // 绑定模型到视图 ui-tableView-setModel(model); ui-tableView-setSelectionMode(QAbstractItemView::SingleSelection); ui-tableView-setSelectionBehavior(QAbstractItemView::SelectRows); }2.2 界面元素布局设计一个简洁实用的界面包含以下组件QTableView显示学生数据按钮组添加、删除、修改、查询等操作查询条件输入框支持按姓名、专业等字段筛选界面布局可以参考以下结构------------------------------------------- | 搜索框[姓名输入] [专业输入] [查询按钮] | ------------------------------------------- | | | QTableView | | | ------------------------------------------- | [添加] [删除] [修改] [保存] [取消] | -------------------------------------------3. 无SQL实现CRUD操作3.1 添加新记录实现添加学生信息的函数void MainWindow::on_addButton_clicked() { // 在末尾插入新行 int row model-rowCount(); model-insertRow(row); // 设置默认值 QModelIndex index model-index(row, 0); ui-tableView-setCurrentIndex(index); ui-tableView-edit(index); }3.2 删除记录安全删除当前选中行void MainWindow::on_deleteButton_clicked() { int row ui-tableView-currentIndex().row(); if (row 0) { QMessageBox::warning(this, 警告, 请先选择要删除的行); return; } int ret QMessageBox::question(this, 确认删除, 确定要删除选中的学生记录吗, QMessageBox::Yes | QMessageBox::No); if (ret QMessageBox::Yes) { model-removeRow(row); model-submitAll(); model-select(); // 刷新视图 } }3.3 修改与保存实现修改保存功能void MainWindow::on_saveButton_clicked() { model-database().transaction(); if (model-submitAll()) { model-database().commit(); QMessageBox::information(this, 成功, 数据已保存); } else { model-database().rollback(); QMessageBox::critical(this, 错误, QString(保存失败%1).arg(model-lastError().text())); } } void MainWindow::on_cancelButton_clicked() { model-revertAll(); }4. 高级功能实现4.1 数据筛选与查询实现按条件查询功能void MainWindow::on_searchButton_clicked() { QString name ui-nameEdit-text(); QString major ui-majorEdit-text(); QString filter; if (!name.isEmpty()) { filter QString(name LIKE %%1%).arg(name); } if (!major.isEmpty()) { if (!filter.isEmpty()) filter AND ; filter QString(major LIKE %%1%).arg(major); } model-setFilter(filter); model-select(); if (model-rowCount() 0) { QMessageBox::information(this, 提示, 没有找到匹配的记录); } }4.2 数据排序实现点击表头排序功能// 在构造函数中添加连接 connect(ui-tableView-horizontalHeader(), QHeaderView::sectionClicked, this, MainWindow::onHeaderClicked); void MainWindow::onHeaderClicked(int logicalIndex) { static bool ascending true; model-sort(logicalIndex, ascending ? Qt::AscendingOrder : Qt::DescendingOrder); ascending !ascending; }4.3 数据验证在提交前添加数据验证bool MainWindow::validateData() { for (int i 0; i model-rowCount(); i) { QString name model-data(model-index(i, 1)).toString(); if (name.isEmpty()) { QMessageBox::warning(this, 验证错误, QString(第%1行姓名不能为空).arg(i1)); ui-tableView-selectRow(i); return false; } } return true; } // 修改保存按钮的点击事件 void MainWindow::on_saveButton_clicked() { if (!validateData()) { return; } // 原有保存逻辑... }5. 实战技巧与性能优化5.1 批量操作优化当需要处理大量数据时可以使用以下技巧提高性能// 批量添加示例 void MainWindow::batchInsert(const QListStudent students) { model-database().transaction(); // 临时关闭自动提交 model-setEditStrategy(QSqlTableModel::OnManualSubmit); for (const auto student : students) { int row model-rowCount(); model-insertRow(row); model-setData(model-index(row, 1), student.name); model-setData(model-index(row, 2), student.age); // 设置其他字段... } if (model-submitAll()) { model-database().commit(); } else { model-database().rollback(); } // 恢复原策略 model-setEditStrategy(QSqlTableModel::OnManualSubmit); }5.2 自定义委托提升用户体验为特定字段使用自定义委托// 为性别字段设置下拉框委托 class GenderDelegate : public QStyledItemDelegate { public: QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem option, const QModelIndex index) const override { QComboBox *editor new QComboBox(parent); editor-addItems({男, 女, 其他}); return editor; } }; // 在构造函数中添加 ui-tableView-setItemDelegateForColumn(3, new GenderDelegate(this));5.3 常见问题排查数据库锁定问题确保每次操作后都正确关闭数据库连接性能瓶颈对于大型数据集考虑使用分页查询数据不一致合理使用事务保证数据完整性6. 项目扩展思路6.1 导出功能实现添加导出到Excel的功能void MainWindow::on_exportButton_clicked() { QString fileName QFileDialog::getSaveFileName(this, 导出Excel, , Excel文件 (*.xlsx)); if (fileName.isEmpty()) return; QXlsx::Document xlsx; // 写入表头 for (int col 0; col model-columnCount(); col) { xlsx.write(1, col1, model-headerData(col, Qt::Horizontal).toString()); } // 写入数据 for (int row 0; row model-rowCount(); row) { for (int col 0; col model-columnCount(); col) { xlsx.write(row2, col1, model-data(model-index(row, col))); } } if (xlsx.saveAs(fileName)) { QMessageBox::information(this, 成功, 导出完成); } else { QMessageBox::warning(this, 错误, 导出失败); } }6.2 多表关联处理虽然QSqlTableModel主要处理单表但我们可以通过视图实现多表关联// 创建视图关联多个表 QSqlQuery query; query.exec(CREATE VIEW IF NOT EXISTS student_view AS SELECT s.id, s.name, s.age, d.name AS department FROM students s LEFT JOIN departments d ON s.dept_id d.id); // 然后模型可以基于视图操作 model-setTable(student_view);6.3 数据统计功能添加简单的统计功能void MainWindow::showStatistics() { int total model-rowCount(); QSqlQuery query; query.exec(SELECT COUNT(DISTINCT major) FROM students); query.next(); int majors query.value(0).toInt(); query.exec(SELECT AVG(age) FROM students); query.next(); double avgAge query.value(0).toDouble(); QMessageBox::information(this, 统计信息, QString(学生总数%1\n 专业数量%2\n 平均年龄%3) .arg(total).arg(majors).arg(avgAge, 0, f, 1)); }在实际项目中我发现QSqlTableModel的setFilter()方法对于简单查询非常方便但对于复杂查询可能不够灵活。这时可以考虑结合QSqlQueryModel使用或者创建数据库视图来简化操作。