QT M/V架构开发实战:QSqlQueryModel/ QSqlTableModel/ QSqlRelationalTableModel介绍
目录
- @[TOC](目录)
- 前言
- 一、初步介绍
- 二、QSqlQueryModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法
- 5.优缺点
- 三、QSqlTableModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法
- 5.优缺点
- 四、QSqlRelationalTableModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法 (示例:employees表有 dept_id外键,关联 departments表的 id和 name字段)
- 5.优缺点
- 五、重要注意事项
目录
- @[TOC](目录)
- 前言
- 一、初步介绍
- 二、QSqlQueryModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法
- 5.优缺点
- 三、QSqlTableModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法
- 5.优缺点
- 四、QSqlRelationalTableModel
- 1.基础定位
- 2.特点
- 3.核心接口
- 4.典型用法 (示例:employees表有 dept_id外键,关联 departments表的 id和 name字段)
- 5.优缺点
- 五、重要注意事项
前言
本文主要介绍的是使用代码生成的情况下对控件的介绍,包括拥有的功能及能修改的样式,也会说明在qtdesiner拖拽控件生成和使用代码生成控件的区别(如果有的话,遇到了的会说),此版本不属于最终版本,以后遇到什么新奇的点会继续更新!本文基于QT官方的文档进行的编写,QT版本为qt 5.14.0,编写环境为Windows11。不得不说官方文档真是个好东西,有时候有些不会的上去一看就能有灵感解决了,可惜没有中文版本的。
一、初步介绍
因为这三个模型类基本都是将SQL数据库中的数据引入模型的,用到的机会也不是很多,所以就放在一起介绍了。
核心目标: 将数据库查询结果或表数据无缝展示在 Qt 的视图控件(如 QTableView、QListView)中,并支持一定程度的数据操作(编辑、插入、删除)。
共同基础:
依赖 Qt SQL 模块: 使用前需包含 ,并在项目文件 (.pro) 中添加 QT += sql。
需要数据库连接: 使用前必须建立有效的数据库连接 (QSqlDatabase)。
视图绑定: 通过 view->setModel(model)将模型绑定到视图。
数据角色: 通过 data(index, role)获取数据,支持 Qt::DisplayRole(显示文本)、Qt::EditRole(编辑值) 等。
只读属性: QSqlQueryModel默认是只读的。QSqlTableModel和 QSqlRelationalTableModel默认是可编辑的(需设置 setEditStrategy)。
列与字段映射: 模型中的列 (column) 对应 SQL 查询结果或数据库表中的字段 (field)。
二、QSqlQueryModel
1.基础定位
用于执行任意 SQL SELECT查询并将结果集展示在视图中。核心是执行查询并获取结果。
2.特点
只读 (默认): 仅用于显示查询结果,不支持通过模型直接修改数据、插入或删除行。
灵活性高: 可以执行任何 SELECT语句,包括多表连接、聚合函数、子查询等复杂查询。
轻量: 主要职责是执行查询和缓存结果。
手动刷新: 调用 setQuery()执行新查询来刷新数据。不自动监听数据库变化。
3.核心接口
// 设置/执行查询 (刷新模型)
void setQuery(const QSqlQuery &query);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());// 获取底层查询对象
QSqlQuery query() const;// 获取记录 (行) 信息
QSqlRecord record() const; // 返回一个空记录,包含字段信息
QSqlRecord record(int row) const; // 获取指定行的记录 (包含所有字段值)// 获取字段名 (列名)
QString headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// 通常需要重写 headerData() 来设置列标题为字段名
4.典型用法
QSqlQueryModel *model = new QSqlQueryModel;
model->setQuery("SELECT id, name, salary FROM employees WHERE department = 'Sales'", myDatabase);
tableView->setModel(model);
// 重写 headerData 以显示字段名
5.优缺点
优点: 灵活,能处理复杂查询。
缺点: 只读,需要手动刷新,不直接支持编辑。
三、QSqlTableModel
1.基础定位
专门用于操作单个数据库表。提供对表中数据的读写访问(CRUD:Create, Read, Update, Delete)。
2.特点
可读写 (默认): 支持通过模型接口 (setData, insertRow, removeRow) 修改数据、插入新行、删除行。
基于单表: 操作对象是数据库中的一个表。
编辑策略 (EditStrategy): 控制修改如何提交到数据库,是核心特性!
QSqlTableModel::OnFieldChange
:字段值改变时立即提交。(不推荐,效率低且易出错)。
QSqlTableModel::OnRowChange
:当用户选择另一行或调用 submit()时提交当前行的所有修改。(常用)。
QSqlTableModel::OnManualSubmit
:所有修改都缓存在模型中,直到显式调用 submitAll()或 revertAll()。(最常用,提供事务控制)。
过滤与排序: 支持通过 setFilter()设置 WHERE 条件,setSort()设置 ORDER BY 条件。调用 select()应用过滤/排序并刷新数据。
自动生成 SQL: 模型内部根据操作自动生成相应的 INSERT, UPDATE, DELETE语句。
3.核心接口
// 设置操作的表和数据库
void setTable(const QString &tableName);
void setDatabase(const QSqlDatabase &db); // 通常在构造函数设置// 设置编辑策略
void setEditStrategy(QSqlTableModel::EditStrategy strategy);// 执行 SELECT 查询 (应用过滤/排序,刷新数据)
bool select();// 设置过滤条件 (WHERE 子句,不带 WHERE 关键字)
void setFilter(const QString &filter);
// 设置排序 (ORDER BY 子句,不带 ORDER BY 关键字)
void setSort(int column, Qt::SortOrder order);// 数据操作
bool insertRow(int row, const QModelIndex &parent = QModelIndex()); // 插入空行
bool removeRow(int row, const QModelIndex &parent = QModelIndex()); // 删除行
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; // 修改单元格// 提交/回滚修改 (根据 EditStrategy)
bool submit(); // 提交当前行修改 (OnRowChange)
bool submitAll(); // 提交所有缓存的修改 (OnManualSubmit)
void revert(); // 回滚当前行修改
void revertAll(); // 回滚所有缓存的修改// 获取表结构信息
QSqlRecord record() const; // 返回一个空记录,包含字段信息
QSqlRecord record(int row) const; // 获取指定行的记录
QString fieldName(int column) const; // 获取列名 (字段名)
4.典型用法
QSqlTableModel *model = new QSqlTableModel(parent, myDatabase);
model->setTable("employees");
model->setEditStrategy(QSqlTableModel::OnManualSubmit); // 推荐
model->setFilter("department = 'Sales'"); // 设置过滤
model->setSort(2, Qt::AscendingOrder); // 按第3列(索引2)升序
model->select(); // 执行查询,加载数据tableView->setModel(model);
tableView->setEditTriggers(QAbstractItemView::DoubleClicked); // 启用视图编辑// 点击保存按钮时提交所有修改
connect(saveButton, &QPushButton::clicked, model, &QSqlTableModel::submitAll);
// 点击取消按钮时回滚所有修改
connect(cancelButton, &QPushButton::clicked, model, &QSqlTableModel::revertAll);
5.优缺点
优点: 支持对单表进行完整的 CRUD 操作,集成度高,使用相对方便。
缺点: 仅限于单表操作。复杂查询(如多表 JOIN)需要绕行或使用其他模型。OnManualSubmit需要手动管理提交。
四、QSqlRelationalTableModel
1.基础定位
继承自 QSqlTableModel。专门用于处理具有外键 (Foreign Key) 关系的单表。它允许你在显示主表记录时,用另一个关联表(Lookup Table)中的友好名称代替原始的外键 ID 值。
2.特点
继承 QSqlTableModel: 拥有 QSqlTableModel的所有功能(CRUD、过滤、排序、编辑策略)。
关系映射 (Relation): 核心功能! 将主表中的外键字段映射到关联表的某个字段(通常是显示名称)。
下拉框支持: 在可编辑的视图(如 QTableView)中,编辑具有关系的字段时,会自动提供一个下拉框 (QComboBox),列出关联表中的可选值。
内部使用 JOIN: 为了实现关系显示,模型内部在执行 SELECT时会使用 LEFT JOIN(或类似)将关联表连接进来。
3.核心接口
// 设置关系 (核心!)
void setRelation(int column, const QSqlRelation &relation);
// QSqlRelation 构造函数: QSqlRelation(tableName, fieldId, fieldDisplay)
// tableName: 关联表名
// fieldId: 关联表中与主表外键字段匹配的 ID 字段 (通常是主键)
// fieldDisplay: 关联表中要显示的友好名称字段// 获取关系
QSqlRelation relation(int column) const;// 获取关联表的模型 (可用于自定义委托等)
QSqlTableModel *relationModel(int column) const;
4.典型用法 (示例:employees表有 dept_id外键,关联 departments表的 id和 name字段)
QSqlRelationalTableModel *model = new QSqlRelationalTableModel(parent, myDatabase);
model->setTable("employees");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);// 设置关系:将 employees 表的第3列 (假设是 dept_id) 映射到 departments 表的 name 字段
model->setRelation(3, QSqlRelation("departments", "id", "name"));model->select();tableView->setModel(model);// 为了让关系列显示友好名称并使用下拉框,通常需要设置一个特殊的委托
tableView->setItemDelegate(new QSqlRelationalDelegate(tableView));/*
效果:
在 tableView中,原本显示 dept_id(如 1, 2) 的列,现在会显示对应的部门名称 (如 "Sales", "Engineering")。
当用户双击该列进行编辑时,会出现一个下拉框 (QComboBox),列出 departments表中所有的 name值供用户选择。用户选择后,模型内部实际存储和操作的仍然是 dept_id。
*/
5.优缺点
优点: 极大地提升了用户界面友好度,尤其是在处理外键关系时。简化了基于外键的选择操作。
缺点: 仍然基于单表(主表),复杂关系或多表 JOIN 显示非友好名称字段可能力不从心。性能可能略低于 QSqlTableModel(因为 JOIN 操作)。
五、重要注意事项
1、事务管理: QSqlTableModel和 QSqlRelationalTableModel的 submitAll()操作通常发生在数据库事务之外。如果需要严格的原子性操作,应在调用 submitAll()前后手动管理 QSqlDatabase的事务 (transaction(), commit(), rollback())。
2、性能: 对于大型数据集,QSqlTableModel和 QSqlRelationalTableModel默认会一次性加载所有数据到内存。考虑使用 setQuery分页 (对 QSqlQueryModel) 或限制过滤条件来优化。
3、复杂场景: 对于非常复杂的数据库交互(如涉及多个表深度修改、存储过程调用、特定数据库特性),直接使用 QSqlQuery结合自定义模型 (QAbstractItemModel) 可能是更灵活的选择。
4、委托: QSqlRelationalDelegate对于关系列的编辑体验至关重要。你也可以创建自定义委托来实现更复杂的关系编辑逻辑。
本次分享就到这里了,如果有什么错误的话请指正,或者有什么疑问的,也可以在评论区一起探讨!