SQL数据库在C++ Qt中的保姆级应用详解
详细介绍了 SQL 数据库的概念,以及其在 Qt 6 项目中的应用
置顶前言
本文将详细带你从零入门 数据库 并将其应用到 Qt 项目
0. 摘要
本文主要介绍 Qt 6 中对数据库的使用和操作,数据库类型为 Qt支持的轻量级数据库SQLite ,不涉及 MySQL 。
本文将以为购物软件构建一个基于 本地账户信息数据库 的 登录/注册 页面。将按照如下顺序展开介绍:
SQL建表 → SQL基本语法 → Qt 6中建立和链接数据库 → Qt 6中对数据库进行操作 → 登录功能的实现 → 注册功能的实现
1. 初识数据库
1.1 什么是数据库?
数据库一词在如今的时代已是家喻户晓了的,可究竟这是个什么东西呢?首先顾名思义,数据库既然是一种 库 , 那么一定是用来 用规律地存放和保管 一些东西的。就像水库管理着水资源,仓库管理着货物资源。数据库自然就是管理各式各样的数据的了。
通过上面的比喻,你对数据库是什么相信大致有了轮廓,接下来我们看看创造 MySQL 的公司 Oracle 是如何定义数据库一词的:

从上面具体的抽象定义中,我们可以具象出如下的图例:

上图很清晰的展示了用户与数据库之间的关系,数据库管理系统(DBMS, Database Management System) 类似于一个坚实可靠的第三方,首先它替我们保存着我们需要的数据,并常常以 .db 格式的文件存储,DB 即 Database,也就是数据库的英文。而在数据库文件中的数据 均以表格(Table) 形式存储,上面的定义也提到了,这是为了方便操作。
DBMS 除了保管数据,它还负责处理所有对数据的操作请求。即当我们,即用户需要使用其中保存的数据库数据时,请求的接收、处理和反馈均由其负责。
而操作数据的请求指令常常是标准的 SQL语句。SQL 即 Structured Query Language,意思是结构化查询语言。是对用户请求的规范化和格式化语言。
注意这里的结构化一词,其实上述的所有定义都是针对结构化数据的数据库的,也即 SQL 数据库 ,又称为关系型数据库。那么聪明的你肯定意识到了还有非结构化数据库的存在,没错,接下来我们就来看看为什么结构化数据库看上去这么完美了还要有人反其道而行之。
1.2 NoSQL 数据库
1.2.1 SQL 数据库的弊病
首先回顾上一节的图例,其中在 DB 中包裹的是 表单数据TB ,正是因为表格这样的数据结构由纵横的行列构成,行伍严整,因此这种数据结构被称为 结构化数据。这样整齐有律的数据结构有什么弊病呢?试想一下这样的应用情景:
假如现在你的任务是为某电商平台构建一个 存储各种商品信息 的数据库,这些商品类目广泛,从日常穿着的衣服鞋帽到虚拟的游戏卡券等,而你需要将他们存储到一个数据库中。
这些相差甚远的商品之间当然有一定共同的要素信息,比如:名称、价格、产地、库存数量
但,虚拟卡券不可能有材质一说,正如衣服鞋帽不可能有游戏平台一说。因此,为了保证存储所有商品的所有信息,你需要为这些 不具有普适性的特征 单独开辟一列用于存储。到这里你可能会说,那有啥,没有这一属性的商品在这一列填 N/A 或者 -1 或者 钝角 不就是了。
但是,你要知道,某一属性不存在 这本质上也是一条数据,也需要占据 某行某列的一个存储单元。接下来以一个具体的例子让你更直观的感受:
你是一名 下北泽的电商新秀 —— 远野池沼 ,现在,你需要将以下海报中的商品信息整理到一个 SQL 数据库 中:

可以看到以上商品信息除了 名称、价格 是固有属性,其余的两个商品特性是较为驳杂的,为了全面的记录信息,你无奈且费劲地整理出了下表:
| 商品名称 | 价格 | 年龄 | 职业 | 特性 | 制作方式 | 用途 | 原价 | 真实性 | 平替性 |
|---|---|---|---|---|---|---|---|---|---|
| 下北泽的野兽先辈 | $114 | 24岁 | 学生 | N/A | N/A | N/A | N/A | N/A | N/A |
| 下北泽的昏睡红茶 | $114 | N/A | N/A | 昏睡 | 现做 | N/A | N/A | N/A | N/A |
| 沈阳大街的冰红茶 | $114 | N/A | N/A | N/A | 冷冻 | 贡品 | N/A | N/A | N/A |
| 二锅头神将神郭嘉 | $114 | N/A | N/A | N/A | N/A | N/A | 15万 | 虚拟 | N/A |
| 下北泽夏夜里的雪 | $114 | N/A | N/A | N/A | 合成 | N/A | N/A | N/A | 平替 |
可以很直观的看到上表的空间利用率是非常差的,我们来计算一下 空间废置率:
空间废置率 = 废弃空间数 总空间数 × 100 % = 45 − 3 × 5 45 × 100 % ≈ 66.7 % \begin{align*} 空间废置率&=\frac{废弃空间数}{总空间数}\times 100\%\\ \\ &=\frac{45-3\times 5}{45}\times 100\%\\ \\ &\approx 66.7\% \end{align*} 空间废置率=总空间数废弃空间数×100%=4545−3×5×100%≈66.7%
假设你的商品不止这五个,而是有着成千上万个商品,并且保持上述的空间废置率,那么你的损失将是非常直观的了。假设我们的数据有 3T 的大小,于是你兴冲冲地买来 3 块西部数据的 1T 硬盘,结果你失望的发现两块的内存装着满满当当的不关心的无用数据,并且最要命的是 这些无用数据的分布还是极其离散的,你还拿他们没辙。
相信通过上面的说明和生动的例子,聪明的你很快就发现了问题所在——结构化数据库在面对海量差异性较大的数据时,空间利用率很差。因此,NoSQL 应版本而生。
1.2.2 解决方案——NoSQL 数据库
为了解决上一节中提到的问题,你猛的想到了 Python中的字典 ,在上例中,其实每个商品都只有三个有效值,那么我们是否可以忽略这三个值究竟归属于哪一类,直接建立由商品名称到这三个值的类似字典中键值对的映射呢。
恭喜你,思路完全正确,NoSQL 数据库泛指一切非关系型数据库,这一大类的数据库常用的数据存储结构如下:
- 键值对存储
- 文档存储
- 图形存储
重复上例,我们用 Python 中的字典来保存这些商品信息,则可以得到如下字典:
goods = {'下北泽的野兽先辈':['$114', '24岁', '学生'],
'下北泽的昏睡红茶':['$114', '昏睡', '现做'],
'沈阳大街的冰红茶':['$114', '冷冻', '贡品'],
'二锅头神将神郭嘉':['$114', '15万', '虚拟'],
'下北泽夏夜里的雪':['$114', '合成', '平替']}
这样简单直接的数据映射关系比之行伍严整的表格数据更加灵活,对于大数据有良好的存储能力。
2. SQL 数据库的数据表建立
2.1 数据库可视化软件——SQLite Expert
上面提到了数据库文件的格式一般为 .db ,一如常见的表格文件格式 .csv, .lxs, xlxs 需要使用像 WPS 或者 Ms Office 这样的可视化软件(P.S. 没给广告费 不加粗 狗头)打开才能直观的查看和操作表格文件一样,数据库文件也需要依赖相应的可视化软件进行查看和操作。
在这里给大伙推荐一款好用的软件——SQLite Expert,这里的 Personal版本 是免费的,可以直接下载使用。
当然,如果你想试试功能更强大的专业版可以在站内搜搜教程,是有关于获取证书下载专业版的详细教程的。
2.2 使用软件建立数据库文件
点击左上方按钮新建一个数据库文件

文件名称当然是随意取的,这里我们继续上一篇的软件,构建 登录/注册 界面,因此需要用到的数据库是账户信息,所以我命名为Accounts。
这里我标红了一处文件夹,在上一篇中提到过使用图片最好用 绝对路径 。现在找到原因了。你建立 Qt 工程的文件夹并非工程所在文件夹。当你使用 QtCreator 构建一个Qt工程时,它会根据你所选的构建版本(比如我选择的是 Debug版本),在同一级目录下构建生成一个新文件夹。因此你所依赖的资源文件最好放到这里,这样便可以通过相对路径读写资源文件了。
此时页面如下:

2.3 为数据库建立一张表格
2.3.1. 反敲左侧栏目中当前的数据库,点击New Tabel

此时进入表格设计页面,如下图所示:

这时一些心急的小伙伴可能直接设置好表格名称便点击了右下角的 Apply ,结果发现报错建立失败了。这是因为关系型数据库的数据表的灵魂便是列。
沿用上面的例子,可以看到我给出的表格中第一行的每一列对应着一种商品属性,如果没有第一行,那么整张表就意义不明了。
因此,我们需要先设置列名,并指定该列的数据存储格式。
2.3.2 为表格设置列名和数据存储格式
首先,按照下图依次点击 columns 和 Add:

然后看到如下添加 数据列 的窗口:

这里可以看到我一共框起来了 4个属性, 其中红色框选的部分为 必填属性, 蓝色框的两个是常用的两个容易搞混的属性。接下来一一介绍。
属性一. 列名 Column Name
这里顾名思义是填写该列的名称的,它的意义是 指明该列中的数据属于哪一类或者说都具有什么特征。
属性二. 数据类型 SQL Type
一如我们在 C++ 声明变量时要声明其类型一样,在添加列到表格中时,我们也需要预先指定要存储的数据类型。下面列举和解释每一个能选择的类型及其含义:
| 数据类型 | 具体解释 |
|---|---|
| ANY | 显而易见,这是最偷懒的类型,即什么都能装。对于 取值实在没有固定类型的数据属性 可以选择这个类型。 |
| BIGINT/LARGEINT | 字如其名,用于存储 超大整数 ,精度为 19 位二进制数。 |
| BINARY | 存储 二进制字串 ,如" 10110101"。 |
| BLOB/BLOB_TEXT | 全名Binary Large Object,用于存储 超大二进制字串 ,最多存放 65,535 字节 的数据。 |
| BOOL/BOOLEAN | 字如其名,用于存储 超大整数 ,精度为 19 位二进制数。 |
| CHAR | 存储 字符或字符串 。 |
| CLOB | 全名Character Large Object,用于存储 超大字符串,最多存放 65,535 字节 的数据。 |
| CURRENCY/MONEY | 存储 货币数据 。具体为 64 位即8 个字节整型 的数值形式,然后 除以 10,000 给出一个定点数 ,其小数点左边有 15 位数字,右边有 4 位数字。 |
| DATE/DATETEXT | 存储日期,格式为 YYYY-MM-DD 支持的数据范围为 1000-01-01 到 9999-12-31。 |
| DATETIME/TIMESTAMP | 日期与时间的组合,格式为 YYYY-MM-DD HH:MM:SS 支持的数据范围为 1000-01-01 00:00:00 到 9999-12-31 23:59:59。 |
| DEC/DECIMAL/NUMERIC | 精确十进制数字 ,可以自己指定 小数点前后的位数(十进制下) 。 |
| DOUBLE/DOUBLE_PRECISION/FLOAT | 近似数值 ,精度为 小数点后16 位二进制数。 |
| GRAPHIC | 双字节字符串 数据, 最大长度 127字节。 |
| GUID | 全局唯一标识符 Global Unique Identifier 格式为 “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx” 其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。 |
| IMAGE/PHOTO/PICTURE | 存储 图片文件 ,存储格式为二进制信息。 |
| INT/INTEGER | 存储 整型数据 ,最长 16 位。 |
| INT64 | 存储 整型数据 ,最长 64 位。 |
| MEMO | 存储 大量文本信息 ,最多可存储 65,536 个字符。 |
| NCHAR | 用于存储 固定长度的Unicode字符串数据 长度范围从 1 到 4,000。 |
| NTEXT | 存储 大量 Unicode 编码的文本信息 。 存储范围为 2 30 − 1 2^{30}-1 230−1 个字符。 |
| NUMBER | 存储 数字数据 ,存储范围大且可自己指定整数和小数位数。 具体范围为 − 1 0 − 130 to 1 0 126 -10^{-130}\text{ to }10^{126} −10−130 to 10126 |
| NVARCHAR | 用于存储 可变长度的Unicode字符串数据 长度范围从 1 到 4,000。 |
| NVARCHAR2 | 用于存储 可变长度的Unicode字符串数据 并且所有字符一律占据 2 字节 长度范围从 1 到 4,000。 |
| ROW | 行名数据 ,理论上相当于 ANY。 |
| REAL | 存储 实数 ,取值近似,小数点后精度为 7 位二进制。 |
| SMALLINT | 较小的数字 ,范围如下 有符号数-32767-32768,无符号数0-65535 。 |
| SMALLMONEY | 存储范围 介于 -214,748.3648-214,748.3647 的 货币数据。 |
| TEXT | 存储 字符串文本信息 。最多 65,535 个字符。 |
| TIME | 存储时间,格式为 HH:MM:SS 支持的数据范围为 -838:59:59 到 838:59:59。 |
| TINYINT | 很小的数字 ,范围如下 有符号数-127-128,无符号数0-255 。 |
| UNIQUEIDENTIFIER | 唯一标识符 Unique Identifier 格式为 “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx” 其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。 |
| VARBINARY | 长度可变二进制字串 数据。 |
| VARCHAR | 长度可变字符/字符串 数据。 英文字符占据 1 个字节,汉字 2 个字节。 |
| VARCHAR2 | 长度可变字符/字符串 数据。 所有字符占据 2 个字节。 |
| WORD | 存储一个字 ,该字由 双字节 构成 。 |
P.S. 各个数据库中关于各类型范围的设置有所不同,上表的类型意义表达准确,范围请以自己使用的数据库文档为准。
属性三. 数据大小 Size
该参数是 传统意义上的长度 ,即值中元素的个数。
- e.g. 123.45678 的 Size 为 8
- e.g. “你干嘛” 的 Size 为 3
属性四. 数据范围 Scale
这个参数主要用来设置 小数点位数(十进制位数)
- e.g. 123.45678 的 Scale 为 5
现在,知道了各个参数的含义之后,我们可以很快地得到 用户名 和 密码 两列。
我的设置参数如下:
| 列名 | 数据类型 | 数据 Size |
|---|---|---|
| User_Name | Text | 50 |
| Password | Text | 20 |
最后,点击 Apply 于是得到一张空白表格如下:

点击左侧建立好的表格之后,再点击 Data 即可以查看表格数据,可以看到我们成功添加了两个列名进来。
2.4 向表格中手动写入数据
接下来,让我们随便为这个表格添加一些数据吧:
Step 1. 插入/追加 数据
下图中左侧按钮为 在当前行的上方插入一行数据, 右侧按钮为 在当前表的最后一行追加一行数据。

Step 2. 编辑列数据
在点击上述任一按钮后,会看到如下界面,此时所有有 … 按钮的数据都是可以输入的。

Step 3. 写入数据并确定
点击 OK 键是确定, Load 是加载数据文件,Save 是保存当前列到数据文件。不要搞糊了,因为我被搞糊过🤬。

Step 4. 写入数据后保存改动到表中
最后一步不要忘了点击这个绿色的勾将你的所有数据变动保存到当前表中。

3. SQL 常用语句
3.1 如何在 SQLite 中执行 SQL 语句
在 SQLite 中是内置了 SQL 编译器的,点击下图中的 SQL 分页键进入编译器:

此时 Ctrl+S 保存,然后自己给代码文件起个名存放一下就好。
3.2 选择语句 —— Select
3.2.1 基础查询
在数据库命令中,最最最最最常用的便是 选择语句 ,即根据一定的规则 提取/选择出所需要的数据。
其基本语法格式如下:
select col_1, col_2, ... from tab;
其中:
col_1, col_2, ...
为所需要的列名,多个时用逗号分隔即可,这里用 * 可以表示所有列
tab
为需要查看的表格名称
例程和效果如下:
- e.g. 提取所有列数据
select * from Users;
点击代码编辑框下方的 Execute 按钮执行 SQL 语句,则有:

- e.g. 提取所有用户名信息
select User_Name from Users;

3.2.2 进阶 —— 条件查询
在实际开发需求中,我们所需的查询选择通常不是取出完整的列数据,而是 在一定条件下提取列数据, 比如提取出 用户名为xxx的所有列数据。
其语法格式如下:
select col_1, col_2, ... from tab where co1_i judge col_j/constant;
where
相当于 C++ 中的 if,表示进行逻辑分支
col_i
表示需要进行逻辑判断的列
judge
表示逻辑判断符号,可以是 >=, ==, <= 等
col_j/constant
表示逻辑表达式右侧的 列数据 或 常量
- e.g. 提取所有用户名为Admin的数据的所有信息
select * from Users where User_Name=="Admin";

3.3 增加数据 —— insert
数据插入方面一共有三种情况,前人之述备矣,具体参考此篇站内博客
这里只以整行插入为例(因为在本项目的账户登录/注册中只用到了这条指令),指令格式如下:
insert into tab values (val_1, val_2, ...);
其中:
tab
为需要查看的表格名称
val_1, val_2, ...
为 按照列名顺序依次填入的 数据
- e.g. 向表格中插入一个用户名为 “Test” 密码为 “0” 的用户数据
insert into Users values ("Test", "0");
执行后点击 data 查看得到如下信息:

3.4 更多 SQL 语句的学习
这里给大家推荐一个由 B站 UP主 程序员鱼皮 搭建的开源免费的SQL学习网站 SQL之母。
4. 在 Qt6 项目中导入 SQL 相关库并初始化
4.1 在 CMakeList.txt 文件中配置 SQL
由于从上一篇开始我的 Qt 工程均采用 QtCreator+CMake 的方式构建,因此 SQL 的引入也需要由 CMakeList.txt 中的指令完成。
- 添加
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Sql REQUIRED) - 在
target_link_libraries中添加Qt${QT_VERSION_MAJOR}::Sql

4.2 相关库的引入
在主窗口头文件中包含引入如下库:
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
QSqlDatabase
用于读取
.db文件,并在程序内构建一个 数据库实例 的库
QSqlQuery
用于向数据库写入 SQL 各种请求指令 的库,实现数据库交互
QSqlError
用于查看 SQL 请求错误信息 的库,方便调试
4.3 数据库的连接/实例化
在 QtSQL 库中,将一个已经存在的 .db 文件读入程序并实例化分为以下三步:
Step 1. 实例化一个 QSqlDatabase 对象
这里语法上十分简单,不需要我说可能聪明的你也想到了,没错,就如实例化任何一个对象一样:
QSqlDatabase db;
就是这么朴实无华,这里唯一需要讲解的点在于 强烈建议将这个对象实例化成全局变量,因为会有很多成员函数以及槽函数需要接入数据库进行操作。比如判断登录是否成功,注册时用户名是否重复等等。
Step 2. 指明数据库类型并读入数据库文件
由于我们此处以 Qt 6 内部自带支持的轻量级数据库类型
QSQLite 为例,因此得到如下代码:
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("Accounts.db");
Step 3. 指明用户名和密码(对于本例随便设置就行)
db.setUserName("Admin");
db.setPassword("12345678");
4.4 为什么是 QSQLite
数据库的类型多种多样,最如雷贯耳的当属 MySQL。但是为什么这里不以它作为例子呢。接下来我们来看看 Qt 6 内置的数据库驱动都有哪些。
这里补充一个调试 Qt 的重要知识点——QDebug。在本系列教程的第一篇我提到过 Qt 中无法使用标准库的输出函数将数据输出到控制台,这并不是因为 Qt 让他失效了,而是 Qt 重定向了输出。
简单来说,你依然可以输出信息到控制台,但只能用 Qt 提供的函数接口,也即 QDebug,使用方法也很简单,在头文件导入 <QDebug>,随后将 qDebug() 函数当作 cout 用即可,例如:
qDebug()<<"Hello, World";
值得一提的是,qDebug() 更类似于 Python 中的 print,因为他有着自动换行的属性。
接下来,通过下面的语句我们便能得到当前工程中 Qt 6 支持的所有数据库类型了:
qDebug()<<"available drivers:";
QStringList drivers = QSqlDatabase::drivers();
foreach(QString driver, drivers)
qDebug()<<driver;
输出结果如下:

可见 MySQL 赫然不在其列,这是由于 MySQL 已经不是完全开源免费的愣头小青年了,因此 Qt 6 开始不再内置其驱动。想连接 MySQL 到 Qt 6 的小伙伴可以自行站内找找教程。
5. 在 Qt6 中操作数据库
通过上面的一系列学习,相信现在你不仅对数据库的相关概念和 SQL 基本语句有了一定的了解,还成功地将 SQL 引入了自己的 Qt 工程项目中。接下来,让我们让 SQL 语句在 Qt 项目中跑起来吧。
5.1 打开数据库
俗话说的好,巧妇难为无米之炊。在成功导入数据库后,第一件要做的事当然是 打开它。实际上,没有人会想只导入数据库而不打开,因此这一步其实在建立和连接到数据库的一瞬间就完成了。
但是再精心编写的程序也会有异常,如果数据库恰好因为某些原因(比如说 驱动问题 等)没有被正常打开,那么我们要如何及时捕获这一异常呢。我们可以直接通过 db.open() 这一内置函数判断数据库是否正常打开,那么又如何得到具体的异常信息呢?这时候 ` 库的便派上用场了。
// Open the Database
void MainWindow::Open_DB(void)
{
if (!db.open()) {
QMessageBox::warning(this, "Database Error",
db.lastError().text());
}
}
在主窗口初始化的函数调用建立数据库的函数后调用这一函数即可在数据库未成功打开时弹出警告窗口。这里也填一下第一篇教程中挖下的坑——细说一下消息提示框—— QMessageBox
5.2 填坑 —— QMessageBox详解
此处仅为概述,深入了解详见 QMessageBox用法详解
5.2.1 导入该库
在你需要为一些错误信息或提示信息 弹出消息窗口时, 只需要在该窗口的头文件中如下引入 QMessageBox 即可:
#include <QMessageBox>
5.2.2 消息框类型
1. 信息提示框 —— QMessageBox::information
调用方式:
QMessageBox::information(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
其中:
parent
指明该提示框将 悬浮在哪个窗口上
title
提示窗口左上角标题
text
提示窗口中的 文本信息
- e.g. 一个简单的例子
QMessageBox::information(this, "Test", "这只是一个例子");

这里还可以注意到,提示弹窗的图标会 继承 parent 的图标
2. 严重错误框 —— QMessageBox::critical
调用方式:
QMessageBox::critical(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
其中参数同上不再赘述。
- e.g. 一个简单的例子
QMessageBox::critical(this, "Test", "这只是一个例子");

3. 错误警告框 —— QMessageBox::warning
调用方式:
QMessageBox::warning(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
其中参数同上不再赘述。
- e.g. 一个简单的例子
QMessageBox::warning(this, "Test", "这只是一个例子");

4. 相关信息框 —— QMessageBox::about
调用方式:
QMessageBox::about(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
其中参数同上不再赘述。
- e.g. 一个简单的例子
QMessageBox::about(this, "Test", "这只是一个例子");

5. 对话请求框 —— QMessageBox::question
调用方式:
QMessageBox::question(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Yes | No,
StandardButton defaultButton = NoButton)
其中参数只有默认按钮与上述不同,这里实际上是两个内置按钮的枚举组合。具体详见本节置顶连接。
- e.g. 一个简单的例子
QMessageBox::question(this, "Test", "这只是一个例子");

P.S. 此处调用方式为最简略的方法,详情请参考本节顶部给出的参考连接
5.3 有始有终 —— 关闭数据库
熟悉文件读写操作的你肯定在看到第一小节 打开数据库 时便敏锐地觉察到了在操作完之后一定需要关闭它。没错,那么什么时候操作完呢?和很显然,在窗口开始销毁时一定不会再操作了。因此我们在 析构函数 中 delete ui; 前加入如下语句关闭数据库:
db.close();
5.4 利用 QSqlQuery 执行 SQL 语句
看到 Query 一词不知道你是否会联想起 js 中的 jQuery,确实二者是有一定类似之处的。本质上都是脚本语言对数据的解析和反馈。接下来我们分三步拆解这一过程:
Step 1. 输入 SQL 语句
这一步的实现非常简单,只需要按照 SQL 语法向 一个 QString 字符串 写入所需要的语句即可。例如:
QString sql_command = "select * from Users;";
Step 2. 执行 SQL 语句
有了需要执行的 SQL 语句之后,我们就需要想办法让他跑起来,类似于需要一个编译执行的编译器,因此,我们可以通过如下语句令我们的 SQL 语句 活起来。
QSqlQuery sql_query;
sql_query.exec(sql_command);
Step 3. 获取返回结果
在执行了语句之后,表格的所有信息则会被 以迭代器的形式 存入 sql_query 这个对象中。因此我们可以通过如下句式访问其中的数据:
while(sql_query.next())
{
// 这里的 toString() 是为了转化成文本信息 具体使用时可以改成自己想要的类型转换
qDebug()<<sql_query.value(col_name).toString();
}
col_name: 指需要查看值的列名。如果需要操作多列则可以在循环中 多次调用上述语句查看每个需要的列即可。
5.5 实例 —— 获取指定用户名的密码
首先,我们可以通过 条件选择 得到指定的密码。但是我们需要考虑一个特殊情况,即 指定的用户名如果不存在应该如何处理。这里我们要知道的是,如果所查询的内容不存在,则 sql_query 中啥都不会有。因此我们可以得到如下代码:
QString MainWindow::Get_Password(QString User_Name){
QString result = "";
// SQL Command Here
QString sql_command = "select Password from Users where User_Name==\"";
sql_command.append(User_Name);
sql_command.append("\"");
// Execute it
QSqlQuery sql_query;
sql_query.exec(sql_command);
// Get the password
while(sql_query.next()){
qDebug()<<sql_query.value("Password").toString();
result.append(sql_query.value("Password").toString());
}
return result;
}
在该函数中,会返回指定用户名的密码查询结果。如果返回结果为空字符串则代表用户不存在。
- P.S. 特别注意上述的条件表达式中的常量值,即指定的用户名值 需要包裹在一堆引号中表明其常量的地位
在主窗口构造函数中如下查询用户名为 “Admin” 的结果如下:

6. 杂谈
6.1 系列教程推荐
本人最火系列 C++ Qt 从入门到如土 教程现已更新至第三篇 —— 登录界面的实现。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)