Qt5 数据库编程详解
个人博客:blogs.wurp.top
1. Qt5 数据库模块概述
QtSql 模块提供了数据库操作的类,主要包括:
- QSqlDatabase:数据库连接
- QSqlQuery:执行 SQL 语句
- QSqlTableModel、QSqlQueryModel:数据库模型类
- QSqlError:数据库错误信息
- QSqlRecord:数据库记录
2. 支持的数据库类型
Qt5 支持多种数据库,包括:
- SQLite
- MySQL/MariaDB
- PostgreSQL
- Oracle
- ODBC (MS SQL Server)
- 其他通过 ODBC 驱动的数据库
3. 数据库连接
3.1 添加数据库驱动
首先需要在项目文件(.pro)中添加 SQL 模块:
QT += sql
3.2 创建数据库连接
#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>// 创建数据库连接
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // 使用 SQLite
db.setDatabaseName("my_database.db"); // 设置数据库文件名// 对于其他数据库,可能需要更多参数
// QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
// db.setHostName("localhost");
// db.setDatabaseName("testdb");
// db.setUserName("user");
// db.setPassword("password");// 打开数据库
if (!db.open()) {qDebug() << "Error: " << db.lastError().text();return false;
}
4. 执行 SQL 查询
4.1 使用 QSqlQuery 执行 SQL
#include <QSqlQuery>// 创建表
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS person (""id INTEGER PRIMARY KEY AUTOINCREMENT, ""name VARCHAR(50) NOT NULL, ""age INTEGER)");// 插入数据 - 使用预处理语句防止 SQL 注入
query.prepare("INSERT INTO person (name, age) VALUES (:name, :age)");
query.bindValue(":name", "John Doe");
query.bindValue(":age", 30);
query.exec();// 查询数据
query.exec("SELECT id, name, age FROM person");
while (query.next()) {int id = query.value(0).toInt();QString name = query.value(1).toString();int age = query.value(2).toInt();qDebug() << id << name << age;
}// 更新数据
query.prepare("UPDATE person SET age = :age WHERE name = :name");
query.bindValue(":age", 31);
query.bindValue(":name", "John Doe");
query.exec();// 删除数据
query.exec("DELETE FROM person WHERE name = 'John Doe'");
5. 使用模型/视图框架
Qt 提供了强大的模型/视图框架,可以方便地将数据库与 UI 组件绑定。
5.1 QSqlTableModel
#include <QSqlTableModel>
#include <QTableView>// 创建模型
QSqlTableModel *model = new QSqlTableModel(this);
model->setTable("person");
model->setEditStrategy(QSqlTableModel::OnManualSubmit); // 设置编辑策略
model->select(); // 加载数据// 创建视图并设置模型
QTableView *view = new QTableView;
view->setModel(model);
view->show();// 添加记录
QSqlRecord record = model->record();
record.setValue("name", "Jane Doe");
record.setValue("age", 25);
model->insertRecord(-1, record);// 提交更改
model->submitAll();
5.2 QSqlQueryModel
#include <QSqlQueryModel>// 创建查询模型
QSqlQueryModel *model = new QSqlQueryModel;
model->setQuery("SELECT name, age FROM person WHERE age > 25");// 设置表头
model->setHeaderData(0, Qt::Horizontal, tr("Name"));
model->setHeaderData(1, Qt::Horizontal, tr("Age"));// 在视图中显示
QTableView *view = new QTableView;
view->setModel(model);
view->show();
6. 事务处理
事务可以确保一系列数据库操作要么全部成功,要么全部失败。
QSqlDatabase::database().transaction(); // 开始事务QSqlQuery query;
query.exec("INSERT INTO person (name, age) VALUES ('Alice', 28)");
query.exec("INSERT INTO person (name, age) VALUES ('Bob', 32)");if (/* 检查操作是否成功 */) {QSqlDatabase::database().commit(); // 提交事务
} else {QSqlDatabase::database().rollback(); // 回滚事务
}
7. 错误处理
正确处理数据库错误非常重要。
QSqlQuery query;
if (!query.exec("SELECT * FROM non_existent_table")) {QSqlError error = query.lastError();qDebug() << "Error type:" << error.type();qDebug() << "Error text:" << error.text();qDebug() << "Database text:" << error.databaseText();qDebug() << "Driver text:" << error.driverText();
}
8. 完整示例代码
下面是一个完整的数据库应用程序示例:
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTableView>
#include <QPushButton>
#include <QLineEdit>
#include <QSpinBox>
#include <QLabel>
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlTableModel>
#include <QHeaderView>
#include <QDebug>class DatabaseApp : public QWidget
{Q_OBJECTpublic:QLineEdit *nameEdit;QSpinBox *ageSpinBox;QTableView *tableView;QSqlTableModel *model;public:DatabaseApp(QWidget *parent = nullptr) : QWidget(parent){// 初始化数据库initDatabase();// 创建UIsetupUI();// 加载数据refreshData();}private slots:void addRecord(){QString name = nameEdit->text().trimmed();int age = ageSpinBox->value();if (name.isEmpty()) {QMessageBox::warning(this, "输入错误", "请输入姓名");return;}QSqlQuery query;query.prepare("INSERT INTO person (name, age) VALUES (?, ?)");query.addBindValue(name);query.addBindValue(age);if (!query.exec()) {QMessageBox::critical(this, "错误", "添加记录失败: " + query.lastError().text());return;}// 清空输入框nameEdit->clear();ageSpinBox->setValue(0);// 刷新显示refreshData();QMessageBox::information(this, "成功", "记录添加成功");}void deleteRecord(){QModelIndexList selected = tableView->selectionModel()->selectedRows();if (selected.isEmpty()) {QMessageBox::warning(this, "警告", "请先选择要删除的记录");return;}int row = selected.first().row();int id = model->data(model->index(row, 0)).toInt();QSqlQuery query;query.prepare("DELETE FROM person WHERE id = ?");query.addBindValue(id);if (!query.exec()) {QMessageBox::critical(this, "错误", "删除记录失败: " + query.lastError().text());return;}refreshData();QMessageBox::information(this, "成功", "记录删除成功");}void refreshData(){model->setTable("person");model->setEditStrategy(QSqlTableModel::OnManualSubmit);model->select();model->setHeaderData(0, Qt::Horizontal, tr("ID"));model->setHeaderData(1, Qt::Horizontal, tr("姓名"));model->setHeaderData(2, Qt::Horizontal, tr("年龄"));}private:void initDatabase(){// 添加SQLite数据库QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName("person.db"); // 使用文件数据库// 打开数据库if (!db.open()) {QMessageBox::critical(nullptr, "数据库错误", "无法打开数据库: " + db.lastError().text());return;}// 创建表QSqlQuery query;QString createTable = "CREATE TABLE IF NOT EXISTS person (""id INTEGER PRIMARY KEY AUTOINCREMENT, ""name VARCHAR(100) NOT NULL, ""age INTEGER)";if (!query.exec(createTable)) {QMessageBox::critical(nullptr, "数据库错误", "创建表失败: " + query.lastError().text());return;}}void setupUI(){// 创建主布局QVBoxLayout *mainLayout = new QVBoxLayout(this);// 创建表单布局QHBoxLayout *formLayout = new QHBoxLayout();QLabel *nameLabel = new QLabel("姓名:");nameEdit = new QLineEdit();QLabel *ageLabel = new QLabel("年龄:");ageSpinBox = new QSpinBox();ageSpinBox->setRange(0, 150);formLayout->addWidget(nameLabel);formLayout->addWidget(nameEdit);formLayout->addWidget(ageLabel);formLayout->addWidget(ageSpinBox);// 创建按钮布局QHBoxLayout *buttonLayout = new QHBoxLayout();QPushButton *addButton = new QPushButton("添加记录");QPushButton *deleteButton = new QPushButton("删除记录");QPushButton *refreshButton = new QPushButton("刷新");buttonLayout->addWidget(addButton);buttonLayout->addWidget(deleteButton);buttonLayout->addWidget(refreshButton);// 创建表格视图tableView = new QTableView();model = new QSqlTableModel(this);tableView->setModel(model);// 设置表格属性tableView->setSelectionMode(QAbstractItemView::SingleSelection);tableView->setSelectionBehavior(QAbstractItemView::SelectRows);tableView->horizontalHeader()->setStretchLastSection(true);// 添加到主布局mainLayout->addLayout(formLayout);mainLayout->addLayout(buttonLayout);mainLayout->addWidget(tableView);// 连接信号和槽connect(addButton, &QPushButton::clicked, this, &DatabaseApp::addRecord);connect(deleteButton, &QPushButton::clicked, this, &DatabaseApp::deleteRecord);connect(refreshButton, &QPushButton::clicked, this, &DatabaseApp::refreshData);// 设置窗口属性setWindowTitle("Qt5 数据库编程示例");resize(600, 400);}};int main(int argc, char *argv[])
{QApplication app(argc, argv);DatabaseApp window;window.show();return app.exec();
}
9. 最佳实践
- 使用预处理语句:防止 SQL 注入攻击,提高性能
- 错误处理:始终检查数据库操作的返回值
- 资源管理:及时关闭数据库连接,释放资源
- 事务处理:对多个相关操作使用事务
- 模型/视图:优先使用模型/视图框架而不是直接执行 SQL
- 数据库连接池:在高并发应用中考虑使用连接池
- 备份和恢复:实现数据库备份和恢复机制
总结
Qt5 提供了强大而灵活的数据库编程支持,通过 QtSql 模块可以轻松连接和操作各种数据库。无论是简单的 SQLite 数据库还是复杂的企业级数据库,Qt5 都能提供一致的 API 和良好的性能。掌握 Qt5 数据库编程对于开发数据驱动的应用程序至关重要。