QT入门第十一天:数据库编程(上)SQLite入门与增删改查 | 零基础学QT
前言
前面十天我们学习了QT界面开发、事件、绘图、文件操作等内容。今天我们来学习一个非常重要的主题:数据库编程。
为什么要学数据库?
- 保存大量结构化数据(比如用户信息、订单记录)
- 数据需要查询、排序、筛选
- 数据要持久化保存
- 多个数据之间有关联关系
文件操作虽然也能保存数据,但处理复杂的结构化数据时,数据库更方便、更高效。
由于数据库内容比较多,我们分成两天来学:
- 今天(上):QT的SQL模块、SQLite入门、数据库连接、增删改查
- 明天(下):QSqlQueryModel、QSqlTableModel、数据与视图绑定、综合实战
一、QT的SQL模块
1.1 QT支持哪些数据库
QT的SQL模块支持多种数据库:
- SQLite:轻量级,无需服务器,文件型数据库(最适合入门和小型应用)
- MySQL:流行的开源数据库
- PostgreSQL:功能强大的开源数据库
- Oracle:企业级数据库
- SQL Server:微软的数据库
- ODBC:通用数据库接口
💡 本教程用SQLite,因为它简单、免安装、QT内置支持,非常适合学习。
1.2 什么是SQLite
SQLite是一个轻量级的数据库:
- 不需要安装数据库服务器
- 整个数据库就是一个文件
- QT内置支持,不用额外配置
- 适合中小型应用
💡 生活类比:MySQL像是一个大型仓库(需要专门的管理员和场地),SQLite像是一个文件柜(一个文件就搞定)。
1.3 添加SQL模块
在.pro文件中添加SQL模块:
QT += sql
如果用CMake(QT6推荐):
cmake
find_package(Qt6 REQUIRED COMPONENTS Sql)
target_link_libraries(mytarget PRIVATE Qt6::Sql)
1.4 相关的类
QT的SQL模块主要有这些类:
| 类 | 用途 |
|---|---|
| QSqlDatabase | 数据库连接 |
| QSqlQuery | 执行SQL语句 |
| QSqlQueryModel | 只读的查询模型 |
| QSqlTableModel | 可读写的表模型 |
| QSqlRecord | 一条记录 |
| QSqlField | 一个字段 |
| QSqlError | 错误信息 |
二、连接数据库
2.1 建立数据库连接
cpp
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
// 添加SQLite数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// 设置数据库文件名(不存在会自动创建)
db.setDatabaseName("mydata.db");
// 打开数据库
if (db.open()) {
qDebug() << "数据库打开成功";
} else {
qDebug() << "数据库打开失败:" << db.lastError().text();
}
💡 说明:
- "QSQLITE" 是SQLite驱动的名称
- setDatabaseName() 设置数据库文件,文件不存在会自动创建
- open() 打开数据库连接
2.2 检查驱动是否可用
cpp
// 查看支持哪些数据库驱动
qDebug() << QSqlDatabase::drivers();
// 检查SQLite驱动是否可用
if (QSqlDatabase::isDriverAvailable("QSQLITE")) {
qDebug() << "SQLite驱动可用";
}
2.3 关闭数据库
cpp
db.close();
程序结束时会自动关闭,但建议不用了就手动关闭。
三、创建表
3.1 什么是表
数据库里的数据是以"表"的形式组织的,就像Excel表格:
- 每一列是一个"字段"(比如姓名、年龄)
- 每一行是一条"记录"(比如一个学生的信息)
3.2 用SQL创建表
cpp
QSqlQuery query;
// 创建学生表
QString sql = "CREATE TABLE IF NOT EXISTS student ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, " // 主键,自增
"name TEXT NOT NULL, " // 姓名,不能为空
"age INTEGER, " // 年龄
"score REAL, " // 分数(小数)
"class TEXT)"; // 班级
if (query.exec(sql)) {
qDebug() << "创建表成功";
} else {
qDebug() << "创建表失败:" << query.lastError().text();
}
3.3 SQLite常用数据类型
| 类型 | 说明 |
|---|---|
| INTEGER | 整数 |
| REAL | 浮点数 |
| TEXT | 文本 |
| BLOB | 二进制数据 |
| NULL | 空值 |
💡 常用约束:
- PRIMARY KEY:主键(唯一标识)
- AUTOINCREMENT:自动增长
- NOT NULL:不能为空
- UNIQUE:唯一
- DEFAULT:默认值
四、增删改查(CRUD)
增删改查是数据库最核心的操作,也叫CRUD:
- Create(增):INSERT
- Read(查):SELECT
- Update(改):UPDATE
- Delete(删):DELETE
4.1 插入数据(增)
方法一:直接拼SQL
cpp
QSqlQuery query;
QString sql = "INSERT INTO student (name, age, score, class) "
"VALUES ('张三', 18, 90.5, '一班')";
if (query.exec(sql)) {
qDebug() << "插入成功";
}
方法二:参数绑定(推荐!更安全)
cpp
QSqlQuery query;
query.prepare("INSERT INTO student (name, age, score, class) "
"VALUES (:name, :age, :score, :class)");
query.bindValue(":name", "李四");
query.bindValue(":age", 19);
query.bindValue(":score", 85.5);
query.bindValue(":class", "二班");
if (query.exec()) {
qDebug() << "插入成功";
}
⚠️ 重要:一定要用参数绑定!这样可以防止SQL注入攻击,也能正确处理特殊字符。
方法三:位置绑定
cpp
QSqlQuery query;
query.prepare("INSERT INTO student (name, age, score, class) "
"VALUES (?, ?, ?, ?)");
query.addBindValue("王五");
query.addBindValue(20);
query.addBindValue(88.0);
query.addBindValue("三班");
query.exec();
4.2 批量插入
cpp
QSqlQuery query;
query.prepare("INSERT INTO student (name, age) VALUES (?, ?)");
QVariantList names;
names << "赵六" << "钱七" << "孙八";
QVariantList ages;
ages << 18 << 19 << 20;
query.addBindValue(names);
query.addBindValue(ages);
// 批量执行
if (query.execBatch()) {
qDebug() << "批量插入成功";
}
4.3 查询数据(查)
cpp
QSqlQuery query;
query.exec("SELECT id, name, age, score FROM student");
// 遍历结果
while (query.next()) {
int id = query.value(0).toInt(); // 按列索引取值
QString name = query.value(1).toString();
int age = query.value(2).toInt();
double score = query.value(3).toDouble();
qDebug() << id << name << age << score;
}
也可以用字段名取值:
cpp
while (query.next()) {
QString name = query.value("name").toString();
int age = query.value("age").toInt();
qDebug() << name << age;
}
4.4 条件查询
cpp
QSqlQuery query;
// 查询年龄大于18的学生
query.prepare("SELECT name, age FROM student WHERE age > :age");
query.bindValue(":age", 18);
query.exec();
while (query.next()) {
qDebug() << query.value("name").toString()
<< query.value("age").toInt();
}
常用的查询条件:
sql
-- 等于
SELECT * FROM student WHERE class = '一班'
-- 大于、小于
SELECT * FROM student WHERE score > 80
-- 范围
SELECT * FROM student WHERE age BETWEEN 18 AND 20
-- 模糊查询
SELECT * FROM student WHERE name LIKE '张%'
-- 多条件
SELECT * FROM student WHERE age > 18 AND score > 85
-- 排序
SELECT * FROM student ORDER BY score DESC
-- 限制数量
SELECT * FROM student LIMIT 10
4.5 更新数据(改)
cpp
QSqlQuery query;
query.prepare("UPDATE student SET score = :score WHERE name = :name");
query.bindValue(":score", 95.0);
query.bindValue(":name", "张三");
if (query.exec()) {
qDebug() << "更新成功,影响行数:" << query.numRowsAffected();
}
4.6 删除数据(删)
cpp
QSqlQuery query;
query.prepare("DELETE FROM student WHERE name = :name");
query.bindValue(":name", "李四");
if (query.exec()) {
qDebug() << "删除成功,影响行数:" << query.numRowsAffected();
}
⚠️ 警告:DELETE不加WHERE条件会删除所有数据!一定要小心。
五、事务处理
5.1 什么是事务
事务是一组操作,要么全部成功,要么全部失败。比如银行转账:A减钱、B加钱,这两个操作必须同时成功或同时失败。
💡 生活类比:事务就像一个"打包操作",要么全做完,要么一个都不做。
5.2 使用事务
cpp
QSqlDatabase db = QSqlDatabase::database();
// 开始事务
db.transaction();
QSqlQuery query;
bool success = true;
// 执行多个操作
success &= query.exec("INSERT INTO student (name) VALUES ('A')");
success &= query.exec("INSERT INTO student (name) VALUES ('B')");
if (success) {
db.commit(); // 提交事务(确认所有操作)
qDebug() << "事务提交成功";
} else {
db.rollback(); // 回滚事务(撤销所有操作)
qDebug() << "事务回滚";
}
5.3 事务的好处
- 保证数据一致性:多个操作要么全成功要么全失败
- 提高批量操作性能:批量插入用事务包起来会快很多
cpp
// 批量插入用事务,速度快很多!
db.transaction();
QSqlQuery query;
query.prepare("INSERT INTO student (name) VALUES (?)");
for (int i = 0; i < 10000; i++) {
query.addBindValue(QString("学生%1").arg(i));
query.exec();
}
db.commit();
六、综合实战:简易通讯录(数据层)
我们来写一个通讯录的数据操作类,封装增删改查。
6.1 完整代码
cpp
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QString>
// 联系人结构
struct Contact {
int id;
QString name;
QString phone;
QString email;
};
// 通讯录数据管理类
class ContactManager
{
public:
// 初始化数据库
bool init() {
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName("contacts.db");
if (!m_db.open()) {
qDebug() << "打开数据库失败:" << m_db.lastError().text();
return false;
}
// 创建表
QSqlQuery query;
QString sql = "CREATE TABLE IF NOT EXISTS contact ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL, "
"phone TEXT, "
"email TEXT)";
if (!query.exec(sql)) {
qDebug() << "创建表失败:" << query.lastError().text();
return false;
}
return true;
}
// 添加联系人
bool addContact(const QString &name, const QString &phone,
const QString &email) {
QSqlQuery query;
query.prepare("INSERT INTO contact (name, phone, email) "
"VALUES (:name, :phone, :email)");
query.bindValue(":name", name);
query.bindValue(":phone", phone);
query.bindValue(":email", email);
if (!query.exec()) {
qDebug() << "添加失败:" << query.lastError().text();
return false;
}
return true;
}
// 删除联系人
bool deleteContact(int id) {
QSqlQuery query;
query.prepare("DELETE FROM contact WHERE id = :id");
query.bindValue(":id", id);
return query.exec();
}
// 更新联系人
bool updateContact(int id, const QString &name,
const QString &phone, const QString &email) {
QSqlQuery query;
query.prepare("UPDATE contact SET name = :name, "
"phone = :phone, email = :email WHERE id = :id");
query.bindValue(":name", name);
query.bindValue(":phone", phone);
query.bindValue(":email", email);
query.bindValue(":id", id);
return query.exec();
}
// 查询所有联系人
QList<Contact> getAllContacts() {
QList<Contact> contacts;
QSqlQuery query("SELECT id, name, phone, email FROM contact");
while (query.next()) {
Contact c;
c.id = query.value("id").toInt();
c.name = query.value("name").toString();
c.phone = query.value("phone").toString();
c.email = query.value("email").toString();
contacts.append(c);
}
return contacts;
}
// 按姓名搜索
QList<Contact> searchByName(const QString &keyword) {
QList<Contact> contacts;
QSqlQuery query;
query.prepare("SELECT id, name, phone, email FROM contact "
"WHERE name LIKE :keyword");
query.bindValue(":keyword", "%" + keyword + "%");
query.exec();
while (query.next()) {
Contact c;
c.id = query.value("id").toInt();
c.name = query.value("name").toString();
c.phone = query.value("phone").toString();
c.email = query.value("email").toString();
contacts.append(c);
}
return contacts;
}
private:
QSqlDatabase m_db;
};
6.2 使用示例
cpp
ContactManager manager;
manager.init();
// 添加联系人
manager.addContact("张三", "13800138000", "zhangsan@qq.com");
manager.addContact("李四", "13900139000", "lisi@qq.com");
// 查询所有
QList<Contact> all = manager.getAllContacts();
for (const Contact &c : all) {
qDebug() << c.id << c.name << c.phone << c.email;
}
// 搜索
QList<Contact> results = manager.searchByName("张");
// 更新
manager.updateContact(1, "张三丰", "13800138000", "new@qq.com");
// 删除
manager.deleteContact(2);
这个类封装了所有的数据库操作,界面层直接调用这些方法就行,数据和界面分离,代码更清晰。
七、今日总结
今天我们学习了QT数据库编程的上篇,主要是SQLite入门和增删改查。
知识点汇总
| 类/操作 | 用途 |
|---|---|
| QSqlDatabase | 数据库连接管理 |
| QSqlQuery | 执行SQL语句 |
| INSERT | 插入数据(增) |
| SELECT | 查询数据(查) |
| UPDATE | 更新数据(改) |
| DELETE | 删除数据(删) |
| transaction/commit | 事务处理 |
重要概念
- ✅ QT += sql:使用SQL模块要先添加
- ✅ QSqlDatabase:建立数据库连接
- ✅ QSqlQuery:执行SQL语句
- ✅ 参数绑定:用bindValue防止SQL注入
- ✅ query.next():遍历查询结果
- ✅ 事务:保证数据一致性,提高批量性能
经验分享
- 一定要用参数绑定:不要直接拼SQL字符串,会有安全隐患
- 检查每一步的返回值:数据库操作可能失败,要处理错误
- 批量操作用事务:速度会快很多
- 数据和界面分离:把数据库操作封装成类,界面层直接调用
- DELETE和UPDATE小心WHERE:不加条件会影响所有数据
- SQLite很适合入门:免安装,一个文件搞定
八、明日预告
明天我们将学习数据库编程(下):模型视图与数据展示。
内容包括:
- QSqlQueryModel:把查询结果显示到表格
- QSqlTableModel:可编辑的表格模型
- 数据库与QTableView绑定
- 数据的排序、筛选
- 综合实战:完整的学生管理系统(带界面)
明天我们把数据库和界面结合起来,做一个真正能用的管理系统!
📝 学习建议:数据库是很多应用的核心,一定要多练。
练习建议:
- 把今天的通讯录数据类代码敲一遍
- 试试自己设计一个"图书管理"的表,实现增删改查
- 试试用事务批量插入1万条数据,感受一下速度
- 试试写各种查询条件(排序、模糊查询、范围查询)
SQL和增删改查掌握了,明天我们把数据库和界面结合起来!明天见,继续加油!💪