Qt 中使用 SQLite实现数据持久化
🗄️ 1. SQLite 数据库介绍
常见的数据库管理系统有:Oracle、SqlServer、MySQL 等。这些数据库管理系统有一个特点,必须先要在本地安装数据库系统的软件,然后才能使用数据库管理系统提供的服务。而数据库管理系统的安装和卸载,有时候简直是噩梦,怎么装就是失败,最后导致重装操作系统。
本文介绍一款轻量级、无需安装的桌面型数据库 SQLite。SQLite 是非常流行的开源嵌入式数据库,将源文件添加到工程就可以直接使用。它很好的支持关系型数据库所具备的一些基本特征,比如:标准 SQL 语法、事务、数据表和索引等。本文中关于SQLite的学习源于作者本人写的一个简洁版的Qt项目MiniMUsic桌面软件,因此本文中的示例代码都是围绕这个项目而编写.
✨ SQLite 主要特征
- 管理简单,甚至可以认为无需管理。
- 操作方便,SQLite 生成的数据库文件可以在各个平台无缝移植。
- 可以非常方便的以多种形式嵌入到其他应用程序中,如静态库、动态库等。
- 易于维护。
Qt 中已经内置了 SQLite,在安装Qt开发环境时,SQLite 环境已经配置好了,用户只需在 .pro 文件中导入数据库模块就可以使用。
// qqmusic.pro QT += sql
🔌 2. QSqlDatabase 类介绍
QSqlDatabase 类主要处理与数据库的连接,它提供了创建、配置、打开和关闭数据库连接的方法。
🔗 2.1 数据库连接和关闭
// 功能:根据 type 来添加数据库驱动
// type:数据库类型 [QDB2:IBM DB2, QMYSQL:MySQL, QOCI:Oracle, QODBC:ODBC, QSQLITE:SQLite...]
// connectionName: 数据库连接的名称[可选]。如果提供,可以为数据库连接指定一个唯一的名称
// 返回值:表示新创建的数据库连接
static QSqlDatabase addDatabase(const QString &type,
const QString &connectionName = QLatin1String(defaultConnection));
// 添加 SQLite 数据库驱动,返回一个连接
QSqlDatabase QQMusicDB = QSqlDatabase::addDatabase("QSQLITE");
// 功能:设置数据库文件的名称
// name: 要连接的数据库的名称。对于 SQLite,通常是数据库文件的路径;对于其他数据库系统,比如 MySQL,
// 通常是数据库管理系统中数据库的名称。
void setDatabaseName(const QString &name);
// 功能:打开数据库连接,即和数据库真正建立连接
// 返回值:连接成功返回 true,否则返回 false,注意:可以使用 isOpen() 方法检测是否打开
// user:数据库用户名
// password: 数据库密码
bool open();
bool open(const QString &user, const QString &password);
// 功能:关闭数据库连接,释放所有资源,并使与数据库一起使用的所有 QSqlQuery 对象无效
void close();数据库连接示例。
测试方式:radioPage 和 musicPage 页面没有使用留待扩展,可以在 radioPage 页面拖一个按钮,objectName 修改为 connectDB,然后添加槽函数,槽函数中实现连接 SQLite 数据库代码:
// qqmusic.h 中新增
void on_connectDB_clicked();
// qqmusic.cpp 中新增
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
void QQMusic::on_connectDB_clicked()
{
// 加载数据库驱动
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// 设置数据库名字
db.setDatabaseName("QQMusic.db");
// 打开数据库
if(!db.open())
{
qDebug() << "QQMusic:" << db.lastError().text();
}
qDebug() << "数据库QQMusic.db创建成功";
// 关闭数据库连接,释放资源
db.close();
}数据库连接成功后,会在构建目录中生成 QQMusic.db 文件
🏗️ 2. 2 创建表
创建表是标准的 SQL 语句,语法如下:
CREATE TABLE tableName(
column1 dataType,
column2 dataType,
column3 dataType ...
);
CREATE TABLE tableName(
column1 dataType PRIMARY KEY AUTOINCREMENT, -- column1为主键,自动增长
column2 dataType NOT NULL, -- 在往表中插入数据时,第二个字段不能为空
column3 dataType,
...
);
-- 创建表,如果表已经存在则不创建
CREATE TABLE IF NOT EXISTS tableName(
column1 dataType PRIMARY KEY AUTOINCREMENT,
column2 dataType NOT NULL,
column3 dataType,
...
);📊 3. SQLite 支持的数据类型类型据类型
| 数据类型 | 描述 |
|---|---|
| NULL | 值是一个 NULL 值 |
| INTEGER | 值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中 |
| REAL | 值是一个浮点数,存储为 8 字节的 IEEE 浮点数 |
| TEXT | 值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储 |
| BLOB | 值是一个 blob 数据,完全根据它的 |
🧑🎓 4.1 创建学生表示例
-- 创建一个学生表,内部包含学生姓名、年龄、性别、成绩
CREATE TABLE IF NOT EXISTS student(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age REAL,
gpa REAL
);数据库表创建好之后,通过 QSqlQuery 类来执行 SQL 语句和操作数据库。QSqlQuery 类中的 exec() 函数用来执行 SQL 语句:
// 功能:执行 SQL 语句 // query: 要实行的 sql 语句,以字符串方式提供 // 返回值:执行成功返回 true,否则返回 false bool exec(const QString &query); // 功能:执行 SQL 语句 // 该方法需要提前将 SQL 语句通过 prepare 方法准备好,然后使用 bindValue 方法绑定参数值, // 最后调用 exec 方法执行 bool exec();
建立数据库连接之后,数据库连接不要关闭,则连接一直存在,可以直接执行 SQL 语句。在 audioPage 页面新增加一个按钮,objectName 修改为:createTable,并添加槽函数,执行如下代码:
// qqmusic.h 中新增
void on_createTable_clicked();
// qqmusic.cpp 中新增
#include <QSqlQuery>
void QQMusic::on_createTable_clicked()
{
// 创建一个学生表,包含:学生id(主键,自动增长), 学生姓名(非空), 年龄,成绩绩点
QString sql("CREATE TABLE IF NOT EXISTS student(\
id INTEGER PRIMARY KEY AUTOINCREMENT,\
name TEXT NOT NULL,\
age INTEGER,\
gpa REAL\
)");
QSqlQuery query;
if(!query.exec(sql))
{
qDebug() << "学生表创建失败:" << query.lastError().text();
return;
}
qDebug() << "创建学生表成功!!!"
➕ 5. 插入数据
往表中插入数据的 SQL 语句:
-- 在这里,column1, column2,...columnN 是要插入数据的表中的列的名称。 INSERT INTO TABLE_NAME [(column1, column2, column3,...columnN)] VALUES (value1, value2, value3,...valueN); -- 如果要为表中的所有列添加值,您也可以不需要在 SQLite 查询中指定列名称。 -- 但要确保值的顺序与列在表中的顺序一致 INSERT INTO TABLE_NAME VALUES (value1,value2,value3,...valueN); INSERT INTO student(id, name, age, gpa) VALUES(1, '张三', 19, 3.5); INSERT INTO student VALUES(2, '李四', 20, 4.0);
🛠️ 5.1 使用 QSqlQuery 类操作类操作
// 功能:准备SQL语句,该语句中包含一个或者多个参数占位符。这些参数占位符在SQL中默认为?表示
// 也可以自定义占位符。其允许提前先设置好SQL语句结构,但是不执行
bool prepare(const QString &query);
// 功能:使用参数的名称(即通过prepare构造SQL语句时设置的占位符)来绑定值
// placeholder: 参数占位符的名称
// val:要绑定的值
void bindValue(const QString &placeholder,
const QVariant &val,
QSql::ParamType paramType = QSql::In);
// 功能:通过位置来帮实际值
// pos: 是参数的位置,从1开始计数
// val: 要绑定的值
void QSqlQuery::bindValue(int pos,
const QVariant &val,
QSql::ParamType paramType = QSql::In);
// 功能:按照建表时成员的顺序绑定
void addBindValue(const QVariant &val,
QSql::ParamType paramType = QSql::In);通过 prepare 先将 SQL 语句准备好,在准备时实际值可以先用其他符号占用,然后通过 bindValue 来绑定实际值,通过命名绑定和位置绑定都可以,绑定好之后,调用 exec 执行。
在 audioPage 页面新增加一个按钮,objectName 修改为:insert,并添加槽函数,执行如下代码:
// qqmusic.h 中新增
void on_insert_clicked();
// qqmusic.cpp 中新增
void QQMusic::on_insert_clicked()
{
QSqlQuery query;
// 通过命名绑定
query.prepare("INSERT INTO student(name, age, gpa) VALUES(:name, :age, :gpa)");
query.bindValue(":name", "张三");
query.bindValue(":age", 19);
query.bindValue(":gpa", 3.5);
if(!query.exec())
{
qDebug() << "学生表插入数据:" << query.lastError().text();
return;
}
qDebug() << "插入数据成功!!!";
// 位置绑定
query.prepare("INSERT INTO student(name, age, gpa) VALUES(?, ?, ?)");
query.bindValue(0, "李四");
query.bindValue(1, 20);
query.bindValue(2, 4.0);
if(!query.exec())
{
qDebug() << "学生表插入数据:" << query.lastError().text();
return;
}
qDebug() << "插入数据成功!!!";
query.prepare("INSERT INTO student(name, age, gpa) VALUES(?, ?, ?)");
query.addBindValue("王五");
query.addBindValue(20);
query.addBindValue(3.8);
if(!query.exec())
{
qDebug() << "学生表插入数据:" << query.lastError().text();
return;
}
qDebug() << "插入数据成功!!!";📚 6. 查询数据
SQLite 的查询语句语法如下:
-- 查询每条记录指定列的值 SELECT column1, column2, columnN FROM table_name; -- 查询结果为表中所有记录 SELECT * FROM table_name;
构造好 SQL 语句,使用 QSqlQuery 的对象 query 执行 SQL 语句,查询结果可以通过 query 获取:
// 功能:将查询结果的当前行指针向后移动一行,如果移动后有记录则返回 true,否则返回 false // 利用该方法,搭配 while 循环,可获取到所有查询记录 bool next(); // 功能:获取查询记录中索引为 index 的域的值 // 查询结果按照 select 语句后所查询字段顺序,从左往右基于 0 开始编号,依次递增 // select name, age, gpa from student; // 每条查询结果中,name 的索引为 0,age 的索引为 1,gpa 的索引为 2 QVariant value(int index) const; // 功能:根据查询记录中,name 字段对应的值,如果名字不匹配,将返回一个非法的 QVariant QVariant value(const QString &name) const;
在 audioPage 页面新增加一个按钮,objectName 修改为:select,并添加槽函数,执行如下代码:
// qqmusic.h 中新增
void on_select_clicked();
// qqmusic.cpp 中新增
void QQMusic::on_select_clicked()
{
// 按照字段查询
QString sql("SELECT name, age, gpa FROM student");
QSqlQuery query;
if(!query.exec(sql))
{
qDebug() << "查询学生表student失败:" << query.lastError().text();
return;
}
// 按照字段名称获取查询到的结果
while(query.next())
{
qDebug() << query.value("name").toString() << " "
<< query.value("age").toInt() << " "
<< query.value("gpa").toDouble();
}
qDebug() << "===================================";
// 查询所有列,按照索引获取
sql = "SELECT * FROM student";
if(!query.exec(sql))
{
qDebug() << "查询学生表student失败:" << query.lastError().text();
return;
}
// 按照字段名称获取查询到的结果
while(query.next())
{
qDebug() << query.value(0).toInt() << " "
<< query.value(1).toString() << " "
<< query.value(2).toInt() << " "
<< query.value(3).toDouble();
}
}SQL 的 SELECT 语句也可以按照指定条件查询。
✏️7. 更新数据
SQLite 中更新数据语法如下:
-- 将指定条件所对应记录的相应列修改为指定值 UPDATE table_name SET column1 = value1, column2 = value2...., columnN = valueN WHERE [condition]; UPDATE student SET age = 21 WHERE name='王五';
在 audioPage 页面新增加一个按钮,objectName 修改为:update,并添加槽函数,执行如下代码:
// qqmusic.h 中新增
void on_update_clicked();
// qqmusic.cpp 中新增
void QQMusic::on_update_clicked()
{
QSqlQuery query;
query.prepare("UPDATE student SET age = :age WHERE name = :name");
query.bindValue(":age", 18);
query.bindValue(":name", "王五");
if(!query.exec())
{
qDebug() << "更新学生信息失败:" << query.lastError().text();
return;
}
qDebug() << "查看更新结果:";
QString sql("SELECT * FROM student WHERE name = '王五'");
if(!query.exec(sql))
{
qDebug() << "查询学生信息失败:" << query.lastError().text();
return;
}
while(query.next())
{
qDebug() << query.value(0).toInt() << " "
<< query.value(1).toString() << " "
<< query.value(2).toInt() << " "
<< query.value(3).toDouble();
}
}⛰️8. 删除数据
SQLite 的删除 SQL 语法如下:
DELETE FROM table_name WHERE [condition]; -- 比如:删除名字为王五的同学 DELETE FROM student WHERE name = '王五';
在 audioPage 页面新增加一个按钮,objectName 修改为:delete,并添加槽函数,执行如下代码:
// qqmusic.h 中添加
void on_deleteRecord_clicked();
// qqmusic.cpp 中添加
void QQMusic::on_deleteRecord_clicked()
{
QSqlQuery query;
query.prepare("DELETE FROM student WHERE name = ?");
query.addBindValue("王五");
if(!query.exec())
{
qDebug() << "删除学生信息失败:" << query.lastError().text();
return;
}
qDebug() << "查看删除结果:";
QString sql("SELECT * FROM student");
if(!query.exec(sql))
{
qDebug() << "查询学生信息失败:" << query.lastError().text();
return;
}
while(query.next())
{
qDebug() << query.value(0).toInt() << " "
<< query.value(1).toString() << " "
<< query.value(2).toInt() << " "
<< query.value(3).toDouble();
}
}操作完成之后,最后别忘记关闭数据库连接,否则
📝 9. 小结
本文围绕 MiniMusic 播放器的数据持久化需求,系统地介绍了如何在 Qt 中使用 SQLite 数据库完成信息的永久保存。核心要点如下:
- 为什么选择 SQLite:相比 Oracle、MySQL 等需要安装的数据库管理系统,SQLite 是一款轻量级、无需安装的嵌入式数据库,Qt 已内置支持,只需在
.pro文件中添加QT += sql即可使用,管理简单、易于维护、跨平台移植方便。 - 数据库连接:通过
QSqlDatabase::addDatabase("QSQLITE")加载驱动,setDatabaseName()指定数据库文件,open()建立连接,close()释放资源。连接成功后会在构建目录生成QQMusic.db文件。 - 建表:使用标准 SQL 的
CREATE TABLE IF NOT EXISTS语句创建表,支持PRIMARY KEY AUTOINCREMENT主键自增、NOT NULL非空约束等特性。SQLite 支持 NULL、INTEGER、REAL、TEXT、BLOB 五种数据类型。 - 增删改查:通过
QSqlQuery类执行 SQL 语句完成数据操作。插入数据支持命名绑定(:name)和位置绑定(?)两种方式;查询通过next()遍历结果集,可用字段名或索引获取值;更新和删除通过WHERE条件精确定位记录。 - 资源管理:操作完成后务必调用
close()关闭数据库连接,避免资源泄漏。
通过这套方案,播放器加载的歌曲、收藏信息、历史播放记录等都能持久化保存,下次启动时自动加载,可以提升用户体验。
🎯 10. 经典面试题
面试题 1:为什么选择 SQLite 而不是 MySQL 或 Oracle?
参考答案:SQLite 是嵌入式、轻量级的数据库,无需安装,将源文件添加到工程即可直接使用,非常适合桌面应用。而 MySQL、Oracle 等需要先在本地安装数据库软件,安装和卸载过程繁琐。此外,SQLite 生成的数据库文件是单个文件,可以在各个平台无缝移植,管理简单、易于维护。Qt 已内置 SQLite 支持,只需在 .pro 文件中添加 QT += sql 即可使用。
面试题 2:如何在 Qt 中建立 SQLite 数据库连接?请写出关键代码。
参考答案:通过 QSqlDatabase 类完成连接,核心步骤如下:
// 1. 加载数据库驱动
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// 2. 设置数据库文件名
db.setDatabaseName("QQMusic.db");
// 3. 打开数据库连接
if(!db.open())
{
qDebug() << "打开数据库失败:" << db.lastError().text();
return;
}
// 4. 使用完毕后关闭连接
db.close();面试题 3:SQLite 支持哪些数据类型?
参考答案:SQLite 支持五种数据类型:
- NULL:空值
- INTEGER:带符号整数,根据大小存储在 1、2、3、4、6 或 8 字节中
- REAL:浮点数,存储为 8 字节 IEEE 浮点数
- TEXT:文本字符串,使用 UTF-8、UTF-16BE 或 UTF-16LE 编码存储
- BLOB:二进制大对象,完全根据输入存储
面试题 4:QSqlQuery 中prepare()、bindValue()和exec()的作用是什么?命名绑定和位置绑定有什么区别?
参考答案:prepare() 用于预编译 SQL 语句,先用占位符(? 或 :name)代替实际值;bindValue() 用于绑定参数值;exec() 用于执行 SQL 语句。命名绑定使用 :name 形式,通过占位符名称绑定值,可读性好;位置绑定使用 ? 形式,按位置(从 1 开始)绑定值,书写简洁。两者效果相同,都能有效防止 SQL 注入。
面试题 5:如何查询 SQLite 中的数据?如何遍历查询结果?
参考答案:使用 QSqlQuery 执行 SELECT 语句,然后通过 next() 方法逐行遍历结果集,使用 value() 方法获取字段值,可以按字段名或索引获取:
QString sql("SELECT name, age, gpa FROM student");
QSqlQuery query;
if(!query.exec(sql))
{
qDebug() << "查询失败:" << query.lastError().text();
return;
}
while(query.next())
{
qDebug() << query.value("name").toString() << " "
<< query.value("age").toInt() << " "
<< query.value("gpa").toDouble();
}面试题 6:如何更新和删除 SQLite 中的记录?
参考答案:更新使用 UPDATE 语句,删除使用 DELETE 语句,两者都需要通过 WHERE 条件指定目标记录:
// 更新:将王五的年龄改为 18
QSqlQuery query;
query.prepare("UPDATE student SET age = :age WHERE name = :name");
query.bindValue(":age", 18);
query.bindValue(":name", "王五");
query.exec();
// 删除:删除名为王五的记录
query.prepare("DELETE FROM student WHERE name = ?");
query.addBindValue("王五");
query.exec();面试题 7:为什么操作完数据库后要关闭连接?不关闭会怎样?
参考答案:数据库连接会占用系统资源(文件句柄、内存等)。如果不调用 close() 关闭连接,会造成资源泄漏,长期运行可能导致系统资源耗尽、程序崩溃。因此,在完成数据库操作后,务必调用 close() 释放资源。在 QQMusic 项目中,数据库连接通常在整个应用生命周期内保持,但程序退出前应确保关闭。
面试题 8:CREATE TABLE IF NOT EXISTS的作用是什么?
参考答案:该语句在创建表之前先检查表是否已存在。如果表已存在,则跳过创建,不会报错;如果不存在,则创建新表。这样可以避免重复建表导致的错误,保证程序多次启动时都能安全执行建表逻辑。
会造成资源泄漏。
到此这篇关于Qt 中使用 SQLite实现数据持久化的文章就介绍到这了,更多相关Qt SQLite持久化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
一款高颜值且免费的 SQL 开发工具之Beekeeper Studio详解
今天给大家推荐一款适用于Windows,Linux和Mac的跨平台免费的开源SQL编辑器和数据库管理应用程序 —— beekeeper-studio。对Beekeeper Studio 安装使用教程感兴趣的朋友一起看看吧2021-09-09
JetBrains出品一款好用到爆的DataGrip数据库工具使用入门
这篇文章主要介绍了JetBrains出品一款好用到爆的DataGrip数据库工具使用入门,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2021-01-01
Navicat Premium 16最新永久激活教程(NavicatCracker)
最新版的Navicat Premium 16 已经发布,今天小编给大家分享Navicat Premium 16最新永久激活教程(NavicatCracker),感兴趣的朋友跟随小编一起看看吧2023-06-06


最新评论