目录
[3.1.2.QSqlQuery:SQL 执行与结果集处理](#3.1.2.QSqlQuery:SQL 执行与结果集处理)
[3.1.3.QSqlRecord: 单行数据的 "容器"](#3.1.3.QSqlRecord: 单行数据的 “容器”)
[3.1.4.QSqlField:单个字段的 "元数据 + 值" 封装](#3.1.4.QSqlField:单个字段的 “元数据 + 值” 封装)
[3.1.5.QSqlIndex:索引 / 主键的 "结构封装"](#3.1.5.QSqlIndex:索引 / 主键的 “结构封装”)
[3.1.6.QSqlError:错误信息的 "标准化封装"](#3.1.6.QSqlError:错误信息的 “标准化封装”)
[3.2.1. QSqlDriver:驱动抽象基类](#3.2.1. QSqlDriver:驱动抽象基类)
3.2.3.QSqlDriverPlugin:数据库驱动插件的抽象基类
[3.3.MVC 模型层(界面绑定类)](#3.3.MVC 模型层(界面绑定类))
1.简介
Qt 的 QSql 模块是 Qt 提供的数据库抽象层,核心目标是屏蔽不同数据库(SQLite、MySQL、PostgreSQL、Oracle、ODBC 等)的底层 API 差异,提供跨平台、统一的数据库交互接口,同时兼容 Qt 核心特性(如信号槽、MVC 架构)。
QSql的核心定位是:
- 抽象层而非 ORM:QSql 聚焦于 "数据库交互的统一接口",而非完整的 ORM(对象关系映射),保留原生 SQL 能力,兼顾灵活性与易用性;
- 插件化驱动模型:通过驱动适配不同数据库,无需修改业务代码即可切换数据库;
- 兼容 Qt 生态 :与 Qt 的 MVC 组件(如
QTableView)、线程模型深度整合。
2.整体架构分层
QSql 模块采用三层架构设计,从上层应用到底层数据库形成清晰的调用链路:
| 层级 | 核心组件 | 职责 |
|---|---|---|
| 应用层(用户层) | 业务代码 | 调用 QSql 提供的 API 完成数据库操作(如查询、事务) |
| 抽象接口层 | QSqlDatabase、QSqlQuery、QSqlError、QSqlRecord | 定义统一的数据库交互接口,屏蔽驱动差异 |
| 驱动适配层 | QSqlDriver(及子类)、QSqlResult | 适配具体数据库的原生 API,实现抽象接口层的方法 |
| 数据库层(底层) | MySQL/SQLite/Oracle 等 | 具体的数据库服务或文件(如 SQLite 数据库文件、MySQL 服务器) |
整体类图如下:

3.核心组件详解
3.1.抽象接口层(核心交互类)
| 类名 | 核心定位 | 核心功能 | 关键关联 / 依赖 |
|---|---|---|---|
| QSqlDatabase | 数据库连接管理器(入口类) | 1. 管理数据库连接(创建、打开、关闭);2. 获取主键索引、表结构;3. 事务控制;4. 加载驱动 | 依赖 QSqlDriver;静态管理全局连接 |
| QSqlQuery | SQL 执行与结果集处理核心类 | 1. 执行 SQL 语句(普通 / 预处理);2. 参数绑定(防注入);3. 遍历结果集;4. 批量操作 | 依赖 QSqlDatabase 和 QSqlResult |
| QSqlError | 错误信息封装类 | 存储错误类型(连接错误 / 执行错误等)、错误文本、数据库原生错误码 | 所有核心类通过 lastError() 返回 |
| QSqlRecord | 单行数据与字段元数据封装 | 1. 存储单行 "字段名 - 值" 映射;2. 访问字段元数据(类型、主键等);3. 新增 / 删除字段 | 组合 QSqlField 列表;被 QSqlQuery/QSqlTableModel 依赖 |
| QSqlField | 单个字段的元数据与值封装 | 1. 存储字段名、类型、值;2. 标记是否为主键 / 可空;3. 空值处理 | 被 QSqlRecord/QSqlIndex 组合 |
| QSqlIndex | 数据库索引(主键 / 普通索引)结构封装 | 1. 描述索引名称、关联表、字段集合;2. 标记排序方式(升序 / 降序);3. 主键判断 |
3.1.1.QSqlDatabase:数据库连接管理器
封装单个数据库连接的句柄,是所有数据库操作的入口。
-
设计要点:
- 连接唯一性:通过「驱动名 + 连接名」标识唯一连接(连接名默认空字符串,即 "默认连接"),进程内通过静态哈希表管理所有连接实例;
- 连接配置:提供
setHostName()、setDatabaseName()、setUserName()等方法配置连接参数; - 连接生命周期:
open()打开连接(调用驱动的open()方法),close()关闭连接,析构时自动关闭; - 事务支持:
transaction()、commit()、rollback()封装事务操作(底层调用驱动的事务接口); - 元数据获取:
tables()、primaryIndex()等方法获取数据库 / 表的元数据(依赖驱动实现)。
-
关键代码示例:
cpp
// 创建 SQLite 连接(驱动名:QSQLITE)
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "my_conn");
db.setDatabaseName("test.db");
if (!db.open()) { // 底层调用 QSQLiteDriver::open()
qDebug() << db.lastError().text(); // 获取错误信息
}
3.1.2.QSqlQuery:SQL 执行与结果集处理
封装 SQL 语句的执行、参数绑定、结果集遍历,是业务层操作 SQL 的核心类。
-
设计要点:
- 预处理语句:支持
prepare()+bindValue()实现参数绑定(位置绑定 / 命名绑定),防止 SQL 注入,提升执行效率; - 结果集遍历:
next()、previous()、first()、last()遍历结果行,value()获取字段值; - 依赖连接:QSqlQuery 必须关联一个已打开的 QSqlDatabase 实例(构造时指定或默认关联);
- 批量操作:
addBindValue()+execBatch()支持批量插入 / 更新,减少网络交互(针对客户端 / 服务器数据库)。
- 预处理语句:支持
-
关键代码示例:
cpp
QSqlQuery query(db); // 关联已打开的连接
// 预处理 + 参数绑定(防止 SQL 注入)
if (!query.prepare("SELECT name, age FROM user WHERE id = :id")) {
qDebug() << query.lastError().text();
}
query.bindValue(":id", 1); // 命名绑定
if (query.exec()) { // 底层调用 QSqlDriver::exec()
while (query.next()) { // 遍历结果集
QString name = query.value(0).toString();
int age = query.value(1).toInt();
qDebug() << name << age;
}
}
3.1.3.**QSqlRecord:**单行数据的 "容器"
封装数据库查询结果的「单行数据」,存储 "字段名 - 字段值" 映射及字段元数据。
- 核心功能 :
- 字段操作:
value()/setValue()读写字段值,isNull()/setNull()处理 NULL 值; - 元数据查询:
fieldName()/indexOf()实现字段名与索引的双向映射。
- 字段操作:
- 典型示例:
cpp
if (query.next()) {
QSqlRecord record = query.record();
QString name = record.value("name").toString(); // 按字段名取值(更易读)
}
3.1.4.QSqlField:单个字段的 "元数据 + 值" 封装
封装单个字段的名称、类型、值、元数据(是否主键、是否可空)。
- 核心功能 :
- 元数据查询:
isPrimaryKey()/isNullable()判断字段属性; - 值操作:
value()/setValue()/setNull()处理字段值。
- 元数据查询:
- 典型场景 :通过
QSqlRecord::field()获取,用于精细处理字段属性。
3.1.5.QSqlIndex:索引 / 主键的 "结构封装"
描述数据库索引(尤其是主键)的名称、关联表、字段集合、排序方式。
- 核心功能 :
- 主键获取:通过
QSqlDatabase::primaryIndex()获取表主键; - 索引构造:
add()添加索引字段,isAscending()判断字段排序方式。
- 主键获取:通过
- 典型示例:
cpp
QSqlIndex pkIndex = db.primaryIndex("user");
qDebug() << "主键字段:" << pkIndex.fieldName(0); // 输出主键字段名(如 id)
3.1.6.QSqlError:错误信息的 "标准化封装"
封装数据库操作的错误信息,所有核心类均可通过 lastError() 返回。
- 核心功能 :
- 错误信息获取:
text()获取错误文本,nativeErrorCode()获取数据库原生错误码,type()判断错误类型(连接错误 / 执行错误等)。
- 错误信息获取:
- 典型示例:
cpp
if (!query.exec()) {
QSqlError err = query.lastError();
qDebug() << "错误类型:" << err.type() << "错误信息:" << err.text();
}
3.2.驱动适配层(底层适配类)
| 类名 | 核心定位 | 核心功能 | 关键关联 / 依赖 |
|---|---|---|---|
| QSqlDriver | 数据库驱动抽象基类 | 定义统一驱动接口:连接数据库、执行 SQL、预处理语句、事务操作等 | 被 QSqlDatabase 依赖;子类需实现纯虚函数 |
| QSqlResult | 结果集处理抽象基类 | 封装驱动层的结果集:遍历行、获取字段值、释放结果集资源 | 被 QSqlQuery 依赖;由驱动子类实现(如 QSQLiteResult) |
| QSqlDriverPlugin | 驱动插件抽象基类(Qt 插件系统适配) | 定义驱动插件的创建接口,用于动态加载驱动(如 qsqlsqlite.dll) |
子类为具体驱动插件(如 QSQLiteDriverPlugin) |
| Qt 内置驱动子类 | 具体数据库的驱动实现(如 QSQLiteDriver/QMYSQLDriver) |
适配原生 API(如 sqlite3_*/mysql_*),实现 QSqlDriver 纯虚函数 |
对应 QSqlResult 子类;通过插件系统加载 |
3.2.1. QSqlDriver:驱动抽象基类
定义数据库驱动的统一接口,是 "抽象接口层" 与 "具体数据库" 的桥梁。
-
设计要点:
- 纯虚函数接口:所有具体驱动必须实现
open()、exec()、prepare()、commit()等核心方法,确保接口统一; - 插件化加载:驱动以 Qt 插件形式存在(如
qsqlsqlite.dll),程序运行时通过 Qt 插件系统加载,QSqlDatabase::drivers()可获取当前可用驱动; - 底层适配:封装具体数据库的原生 API(如 SQLite 的
sqlite3_*、MySQL 的mysql_*函数); - 错误处理:通过
lastError()返回驱动层的错误信息(封装为QSqlError)。
- 纯虚函数接口:所有具体驱动必须实现
-
Qt 内置驱动:
| 驱动名 | 对应数据库 | 核心依赖 |
|---|---|---|
| QSQLITE | SQLite | 内置(无需额外依赖) |
| QMYSQL | MySQL/MariaDB | MySQL C API |
| QPSQL | PostgreSQL | libpq |
| QODBC | ODBC 兼容数据库 | 系统 ODBC 驱动 |
| QOCI | Oracle | Oracle OCI 库 |
3.2.2.QSqlResult:结果集处理抽象基类
封装数据库查询结果集的底层操作(遍历行、获取字段值、释放结果集资源等),是 QSqlQuery(上层 SQL 执行类)与具体数据库驱动(如 QSQLiteDriver)之间的 "中间层"。用户通常无需直接使用 QSqlResult,但理解它能清晰掌握 QSqlQuery 处理结果集的底层逻辑,也是自定义数据库驱动的核心类之一。
设计要点:
- 接口抽象化:所有核心结果集操作均定义为纯虚函数,具体驱动子类必须实现(保证跨数据库接口统一);
- 状态管理 :内置结果集状态(如 "是否有效""是否在首行 / 末行""是否有结果集"),通过
isValid()/isActive()等方法暴露; - 底层资源封装 :子类需封装数据库原生结果集句柄(如 SQLite 的
sqlite3_stmt*、MySQL 的MYSQL_RES*),并在析构时释放资源; - 错误处理 :内置
QSqlError成员,通过lastError()返回驱动层的结果集操作错误。
典型子类实现(Qt 内置)
Qt 为每个内置驱动提供了 QSqlResult 的子类,封装具体数据库的结果集操作:
| 子类 | 对应数据库 | 底层结果集句柄 | 核心实现特点 |
|---|---|---|---|
QSQLiteResult |
SQLite | sqlite3_stmt* |
基于 SQLite 预处理语句接口实现,next() 调用 sqlite3_step(),value() 调用 sqlite3_column_*() |
QMYSQLResult |
MySQL | MYSQL_RES*/MYSQL_STMT* |
区分普通查询(mysql_store_result)和预处理查询(mysql_stmt_*),适配两种结果集 |
QPSQLResult |
PostgreSQL | PGresult* |
基于 libpq 接口,next() 遍历 PGresult 的行,value() 解析字段值 |
QODBCResult |
ODBC | SQLHSTMT |
基于 ODBC 标准接口,通过 SQLFetch() 遍历行,SQLGetData() 获取字段值 |
3.2.3.QSqlDriverPlugin:数据库驱动插件的抽象基类
QSqlDriverPlugin 是 Qt QSql 模块中数据库驱动插件的抽象基类 ,是 Qt 插件系统与数据库驱动之间的 "桥梁"。它遵循 Qt 插件框架规范,核心作用是让 Qt 能够动态加载不同数据库的驱动插件 (如 qsqlsqlite.dll、qsqlmysql.dll),并创建对应的 QSqlDriver 实例。普通开发者无需直接使用,但自定义数据库驱动时,必须继承该类实现插件逻辑。
设计要点:
- 插件体系依赖 :继承自
QObject和QPluginInterface(Qt 插件核心接口),必须遵循 Qt 插件开发规范(如声明元数据、导出接口); - 工厂方法模式 :纯虚函数
create()是核心 "工厂方法",接收驱动名(如 "QSQLITE"),返回对应QSqlDriver实例; - 元数据强制要求 :必须通过
Q_PLUGIN_METADATA宏声明插件元数据,且 IID 需匹配 Qt 定义的QSqlDriverFactoryInterface_iid(保证 Qt 插件系统识别); - 动态加载特性 :Qt 启动时会扫描
sqldrivers插件目录(可通过QT_PLUGIN_PATH自定义),自动加载符合规范的QSqlDriverPlugin插件; - 无状态设计 :插件类仅负责创建驱动实例,不持有驱动状态,驱动的生命周期由
QSqlDatabase管理。
Qt 内置驱动插件示例
Qt 为所有内置数据库驱动实现了 QSqlDriverPlugin 子类,以下是核心对应关系:
| 插件子类 | 驱动名 | 对应数据库 | 核心作用 |
|---|---|---|---|
QSQLiteDriverPlugin |
QSQLITE | SQLite | 创建 QSQLiteDriver 实例 |
QMYSQLDriverPlugin |
QMYSQL | MySQL | 创建 QMYSQLDriver 实例 |
QPSQLDriverPlugin |
QPSQL | PostgreSQL | 创建 QPSQLDriver 实例 |
QODBCDriverPlugin |
QODBC | ODBC | 创建 QODBCDriver 实例 |
这些插件被编译为动态库(如 Windows 下的 qsqlsqlite.dll、Linux 下的 libqsqlsqlite.so),存放于 Qt 安装目录的 sqldrivers 子目录。
3.2.4.自定义驱动
若 Qt 未提供某数据库的驱动(如小众自研数据库),需继承 QSqlDriverPlugin 实现自定义插件,以下是核心步骤:
1.实现自定义 QSqlDriver 子类
cpp
// mycustomdriver.h
#include <QSqlDriver>
class MyCustomDriver : public QSqlDriver
{
Q_OBJECT
public:
explicit MyCustomDriver(QObject *parent = nullptr) : QSqlDriver(parent) {}
// 必须实现的纯虚函数(示例仅占位,需适配目标数据库API)
bool open(const QString &db, const QString &user, const QString &password,
const QString &host, int port, const QString &connOpts) override {
// 实现数据库连接逻辑(调用目标数据库的原生open API)
return true;
}
void close() override {
// 实现数据库关闭逻辑
}
QSqlResult *createResult() const override {
// 返回自定义 QSqlResult 子类实例(适配结果集处理)
return new MyCustomResult();
}
// 其他纯虚函数(如 exec()、prepare() 等)需逐一实现
};
// 自定义 QSqlResult 子类(必须)
class MyCustomResult : public QSqlResult
{
public:
explicit MyCustomResult(const QSqlDriver *drv) : QSqlResult(drv) {}
// 实现 QSqlResult 的纯虚函数(next()、value()、clear() 等)
bool next() override { return false; }
QVariant value(int index) override { return QVariant(); }
bool isValid() const override { return false; }
// ... 其他纯虚函数
};
2.实现 QSqlDriverPlugin 子类
继承 QSqlDriverPlugin,实现 create() 方法,返回自定义 MyCustomDriver 实例:
cpp
// mycustomdriverplugin.h
#include <QSqlDriverPlugin>
#include "mycustomdriver.h"
class MyCustomDriverPlugin : public QSqlDriverPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "mycustomdriver.json")
// 注意:IID 必须与 Qt 内置的 QSqlDriverFactoryInterface_iid 完全一致
public:
QSqlDriver *create(const QString &key) override {
// 匹配自定义驱动名(如 "MYCUSTOM")
if (key == "MYCUSTOM") {
return new MyCustomDriver();
}
return nullptr; // 不匹配则返回空
}
};
3.3.MVC 模型层(界面绑定类)
| 类名 | 核心定位 | 核心功能 | 关键关联 / 依赖 |
|---|---|---|---|
| QSqlQueryModel | 只读 MVC 模型(适配查询结果集) | 1. 封装 QSqlQuery 结果集;2. 适配 QTableView 等视图;3. 支持排序 / 筛选 |
继承 QAbstractTableModel;依赖 QSqlQuery |
| QSqlTableModel | 可编辑 MVC 模型(适配整张表) | 1. 封装表的增删改查(无需手写 SQL);2. 支持事务提交、批量操作;3. 字段验证 | 继承 QSqlQueryModel;依赖 QSqlDatabase/QSqlRecord |
| QSqlRelationalTableModel | 支持外键关联的可编辑模型 | 1. 扩展 QSqlTableModel;2. 自动关联外键表数据(如显示名称而非 ID);3. 级联操作 |
继承 QSqlTableModel;依赖 QSqlRelation/QSqlIndex |
| QSqlRelation | 外键关联规则封装类 | 定义 "主表 - 外键字段 - 显示字段" 映射(如 order.user_id → user.id → user.name) |
被 QSqlRelationalTableModel 依赖 |
3.3.1.QSqlQueryModel:只读模型
QSqlQueryModel 是 Qt QSql 模块中只读的数据库 MVC 模型 ,核心作用是封装 QSqlQuery 的查询结果集,并适配 Qt 的视图组件(如 QTableView/QListView),实现 "数据库查询结果 → 界面展示" 的一键绑定。它是连接 QSqlQuery(底层 SQL 执行)与 Qt 视图(上层展示)的桥梁,只读 是其核心特性(默认不支持编辑),适合仅需展示查询结果的场景。
类继承关系:
QObject → QAbstractItemModel → QAbstractTableModel → QSqlQueryModel
核心设计要点:
1.内部机制
- 调用
setQuery()时,内部创建QSqlQuery执行 SQL,缓存结果集的行数、列数及字段元数据; - 视图请求数据(如
QTableView渲染)时,data()方法从QSqlQuery的结果集中取值并返回; - 所有数据操作均通过
QSqlQuery完成,模型仅做 "数据中转"。
2.只读特性
重写 setData() 方法并默认返回 false,拒绝编辑操作;若需编辑,需继承并重写 setData()(但更推荐使用 QSqlTableModel)。
3.信号联动
执行 setQuery() 后会发出 modelReset 信号,触发视图重新加载数据;结果集变化时自动同步界面。
典型示例:
最常用场景:执行 SQL 查询,将模型绑定到 QTableView 展示结果,无需手动遍历结果集。
cpp
#include <QApplication>
#include <QSqlQueryModel>
#include <QTableView>
#include <QSqlDatabase>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 1. 初始化数据库连接(SQLite 示例)
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("test.db");
if (!db.open()) {
qDebug() << "数据库连接失败:" << db.lastError().text();
return -1;
}
// 2. 创建 QSqlQueryModel 并执行查询
QSqlQueryModel *model = new QSqlQueryModel;
// 执行复杂查询(支持多表关联、聚合等,QSqlTableModel 不支持)
model->setQuery("SELECT id, name, age, create_time FROM user WHERE age > 18", db);
// 检查查询错误
if (model->lastError().isValid()) {
qDebug() << "查询失败:" << model->lastError().text();
return -1;
}
// 3. 自定义表头显示(可选)
model->setHeaderData(0, Qt::Horizontal, "用户ID");
model->setHeaderData(1, Qt::Horizontal, "姓名");
model->setHeaderData(2, Qt::Horizontal, "年龄");
model->setHeaderData(3, Qt::Horizontal, "创建时间");
// 4. 绑定模型到 QTableView 并显示
QTableView *view = new QTableView;
view->setModel(model);
view->resizeColumnsToContents(); // 自适应列宽
view->show();
return a.exec();
}
自定义数据显示格式(重写 data() 方法)
默认 data() 仅返回原始值,可继承 QSqlQueryModel 重写 data(),实现自定义显示(如布尔值转文字、日期格式化、数值单位拼接)。
cpp
#include <QSqlQueryModel>
#include <QDateTime>
#include <QColor>
// 自定义查询模型
class CustomSqlQueryModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit CustomSqlQueryModel(QObject *parent = nullptr) : QSqlQueryModel(parent) {}
// 重写 data 方法,自定义显示逻辑
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
// 1. 获取原始值
QVariant value = QSqlQueryModel::data(index, role);
if (!index.isValid() || value.isNull()) {
return QVariant();
}
// 2. 按角色和列自定义显示
switch (role) {
case Qt::DisplayRole: {
// 列2(年龄):拼接单位
if (index.column() == 2) {
return QString("%1 岁").arg(value.toInt());
}
// 列3(创建时间):格式化日期
else if (index.column() == 3) {
QDateTime dt = value.toDateTime();
return dt.toString("yyyy-MM-dd HH:mm:ss");
}
break;
}
case Qt::TextColorRole: {
// 列2(年龄):大于30岁显示红色
if (index.column() == 2 && value.toInt() > 30) {
return QColor(Qt::red);
}
break;
}
case Qt::TextAlignmentRole: {
// 所有列居中对齐
return Qt::AlignCenter;
}
}
// 其他角色/列返回原始值
return value;
}
};
// 使用自定义模型
// CustomSqlQueryModel *model = new CustomSqlQueryModel;
// model->setQuery("SELECT id, name, age, create_time FROM user", db);
// view->setModel(model);
3.3.2.QSqlTableModel
QSqlTableModel 是 Qt QSql 模块中面向单表的可编辑 MVC 模型 ,核心定位是封装单个数据库表的增删改查(CRUD)操作,无需手写 SQL 语句,支持灵活的编辑策略、筛选、排序,并深度适配 Qt 视图组件(如 QTableView)。它继承自 QSqlQueryModel,但突破了 "只读" 限制,是单表数据展示与编辑场景的首选模型。
继承关系:
QObject → QAbstractItemModel → QAbstractTableModel → QSqlQueryModel → QSqlTableModel
设计要点:
1.编辑策略(核心特性)
通过 setEditStrategy() 控制数据修改的提交时机,Qt 提供三种预设策略:
| 策略常量 | 含义 | 适用场景 |
|---|---|---|
QSqlTableModel::OnFieldChange |
字段值修改后立即提交到数据库(失去焦点 / 回车时) | 实时同步、修改频率低的场景 |
QSqlTableModel::OnRowChange |
整行编辑完成后(切换行时)提交到数据库 | 单行多字段编辑、减少提交次数 |
QSqlTableModel::OnManualSubmit |
仅调用 submitAll() 时提交,revertAll() 可回滚所有未提交修改 |
批量编辑、需原子性提交的场景 |
2.自动 SQL 生成
底层根据操作自动生成标准 SQL(如 SELECT * FROM table、UPDATE table SET ...),无需手动编写,且自动处理参数绑定(防 SQL 注入)。
3.主键依赖
模型依赖表的主键实现行更新 / 删除(通过主键定位行),若表无主键,setData()/removeRow() 可能失效。
4.涉及的设计模式
QSql 模块大量采用设计模式保证扩展性和易用性:
1.抽象工厂模式(Abstract Factory)
- 角色映射 :
- 抽象工厂:
QSqlDatabase(通过addDatabase()创建驱动实例); - 具体工厂:各驱动的
QSqlDriverCreator(Qt 内部实现,用于创建驱动实例); - 抽象产品:
QSqlDriver; - 具体产品:
QSQLiteDriver、QMYSQLDriver等。
- 抽象工厂:
- 作用:用户仅需指定驱动名(如 "QSQLITE"),即可创建对应数据库的驱动实例,无需感知底层实现。
2.适配器模式(Adapter)
- 角色映射 :
- 目标接口:
QSqlDriver; - 适配器:
QSQLiteDriver/QMYSQLDriver等; - 适配者:具体数据库的原生 API(如
sqlite3_exec()、mysql_query())。
- 目标接口:
- 作用 :将不同数据库的原生 API 适配为统一的
QSqlDriver接口,屏蔽底层差异。
3.MVC 模式
- 角色映射 :
- 模型(Model):
QSqlQueryModel/QSqlTableModel; - 视图(View):
QTableView/QListView等; - 控制器(Controller):用户交互逻辑(如按钮触发的增删改查)。
- 模型(Model):
- 作用:分离数据与展示,简化界面开发,符合 Qt 界面编程的核心思想。
4.注册表模式(Registry)
- 实现 :
QSqlDatabase内部维护一个静态QHash<QString, QSqlDatabase>,以连接名为键存储所有连接实例; - 作用:全局管理连接实例,确保连接名唯一,支持跨模块复用连接。
5.线程安全设计
QSql 模块的线程安全是高频问题,核心设计原则:
- QSqlDatabase 非线程安全 :
- 不能跨线程共享同一个 QSqlDatabase 实例;
- 每个线程需创建独立的连接实例(通过不同的连接名,如 "thread1_conn"、"thread2_conn");
- QSqlQuery 依赖连接 :
- QSqlQuery 与 QSqlDatabase 强绑定,因此也不能跨线程使用;
- 驱动层的线程安全 :
- 不同驱动的线程安全特性不同(如 SQLite 驱动默认单线程,需开启
SQLITE_THREADSAFE编译选项); - 客户端 / 服务器数据库(如 MySQL)的驱动线程安全依赖底层 API(如 libmysqlclient 是线程安全的)。
- 不同驱动的线程安全特性不同(如 SQLite 驱动默认单线程,需开启
- 线程中使用数据库的最佳实践:
cpp
// 线程类
class DbThread : public QThread {
void run() override {
// 线程内创建独立连接
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "thread_conn");
db.setDatabaseName("test.db");
if (db.open()) {
QSqlQuery query(db);
query.exec("SELECT * FROM user");
// 处理结果...
db.close();
}
// 移除连接(避免内存泄漏)
QSqlDatabase::removeDatabase("thread_conn");
}
};
6.扩展与定制
QSql 模块支持灵活扩展,满足定制化需求:
1. 自定义数据库驱动
若 Qt 未提供某数据库的驱动,可自定义驱动:
- 步骤 1:继承
QSqlDriver,实现所有纯虚函数(open()、exec()、prepare()等); - 步骤 2:继承
QSqlDriverPlugin,实现create()方法返回自定义驱动实例; - 步骤 3:编译为 Qt 插件(放到 Qt 的
sqldrivers插件目录); - 步骤 4:通过
QSqlDatabase::addDatabase("自定义驱动名")加载。
2.自定义模型
继承 QSqlTableModel/QSqlQueryModel,重写核心方法:
data():自定义字段显示(如将 0/1 转为 "否 / 是");setData():自定义编辑逻辑(如数据校验);filter():自定义筛选规则。
3.实现数据库连接池
Qt 原生未提供连接池,可基于 QSqlDatabase 封装:
- 维护一个可用连接的队列;
- 线程从池获取连接,使用后归还;
- 自动检测失效连接,重新创建。
7.注意事项
1.使用注意
- 参数绑定优先 :使用
prepare()+bindValue()而非拼接 SQL 字符串,防止 SQL 注入; - 连接复用:避免频繁打开 / 关闭连接,建议使用连接池;
- 错误处理 :所有数据库操作后检查
lastError(),避免静默失败; - 事务批量操作:批量插入 / 更新时开启事务,减少磁盘 IO / 网络交互;
- 元数据适配 :跨数据库时通过
QSqlDatabase::tables()等元数据方法适配,减少硬编码。
2.局限性
- 高级特性支持不足:对数据库特有特性(如 MySQL 存储过程输出参数、Oracle LOB 类型)支持有限,需直接调用驱动底层 API;
- SQL 语法差异 :QSql 仅封装接口,SQL 语法仍需遵循标准,不同数据库的扩展语法(如
LIMITvsROWNUM)需手动适配; - 性能开销:相比原生 API 有轻微封装开销,超高并发场景需优化(如批量操作、索引设计);
- ORM 缺失 :无内置 ORM,复杂业务需手动映射对象与数据库表(可结合第三方库如
Qt ORM、SqliteOrm)。
8.总结
QSql 模块的核心设计思路是 **"抽象统一接口 + 插件化驱动"**,通过分层架构屏蔽数据库差异,同时兼容 Qt 的 MVC、线程模型等核心特性。其优势在于跨平台、易用性强,适合中小规模数据库应用;若需处理复杂业务或高性能场景,可结合自定义驱动、连接池、第三方 ORM 扩展。