目录
一、开发成果
二、环境配置与基础概念
1. 引入SQL模块
在Qt项目文件(.pro)中添加SQL模块支持,这是操作数据库的前提:
QT += sql
该语句启用了Qt的数据库驱动功能,支持SQLite、MySQL等数据库。
2. SQLite数据库特性
SQLite无需独立服务器进程,数据以单个文件形式存储(扩展名通常为.db),支持事务、零配置,适合嵌入式和小型应用。其轻量级特性使其成为Qt开发中的首选数据库之一。
三、数据库连接与操作流程
1. 创建并连接数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // 指定驱动类型
db.setDatabaseName("mydatabase.db"); // 设置数据库文件路径
if (!db.open()) {
qDebug() << "连接失败:" << db.lastError().text();
return;
}
- 关键点:
- 使用QSQLITE驱动标识符。
- setDatabaseName()可指定绝对路径或内存数据库(如:memory:)。
- 若文件不存在,SQLite会自动创建新数据库。
2. 执行SQL语句
通过QSqlQuery类执行SQL命令:
QSqlQuery query;
// 创建表
query.exec("CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL, age INT)");
// 插入数据(预编译防SQL注入)
query.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
query.bindValue(":name", "Alice");
query.bindValue(":age", 30);
if (!query.exec()) {
qDebug() << "插入失败:" << query.lastError().text();
}
- 方法对比:
- 直接执行exec(“SQL语句”)适合简单操作。
- prepare() + bindValue()更安全,支持参数绑定,防止SQL注入。
3. 查询与遍历数据
query.exec("SELECT * FROM users");
while (query.next()) {
int id = query.value("id").toInt();
QString name = query.value(1).toString(); // 通过字段名或索引获取
int age = query.value(2).toInt();
qDebug() << id << name << age;
}
- 注意:
- query.value()返回QVariant类型,需转换为具体数据类型。
- 使用query.record()可获取字段元数据(如字段名、类型)。
四、进阶操作与最佳实践
1. 事务处理
确保数据一致性:
db.transaction(); // 开启事务
// 执行多条SQL操作
if (所有操作成功) {
db.commit(); // 提交事务
} else {
db.rollback(); // 回滚事务
}
事务适用于批量插入、更新等场景,避免部分操作失败导致数据不一致。
2. 错误处理
每次操作后检查错误:
if (!query.exec()) {
qDebug() << "错误信息:" << query.lastError().text();
qDebug() << "执行的SQL:" << query.lastQuery();
}
通过lastError()获取详细错误描述,便于调试。
3. 使用模型/视图架构
Qt提供QSqlTableModel和QSqlQueryModel,实现数据库与UI组件的绑定:
QSqlTableModel *model = new QSqlTableModel(this);
model->setTable("users");
model->select();
QTableView *view = new QTableView;
view->setModel(model);
通过模型类可自动同步数据到表格视图,简化开发。
五、完整代码示例(学生人员管理)
1.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTableView>
#include <QLineEdit>
#include <QComboBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlTableModel>
#include <QMessageBox>
class DatabaseManager {
public:
static DatabaseManager* getInstance() {
if (!instance) {
instance = new DatabaseManager();
}
return instance;
}
QSqlDatabase getDatabase() const {
return db;
}
private:
DatabaseManager() {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("students.db");
if (!db.open()) {
QMessageBox::critical(nullptr, "Cannot open database",
"Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit.", QMessageBox::Cancel);
} else {
createTable();
}
}
~DatabaseManager() {
db.close();
}
void createTable() {
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS students (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, gender TEXT)");
}
static DatabaseManager* instance;
QSqlDatabase db;
};
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
void setHeadersForQueryModel(QSqlQueryModel *queryModel);
QSqlTableModel *m_model;
QSqlQueryModel *m_queryModel;
QTableView *m_tableView;
QLineEdit *m_nameEdit;
QLineEdit *m_ageEdit;
QComboBox *m_genderCombo;
};
#endif // MAINWINDOW_H
2.mainwindow.cpp
#include "mainwindow.h"
DatabaseManager* DatabaseManager::instance = nullptr;
MainWindow::MainWindow(QWidget *parent)
: QWidget(parent),
m_model(new QSqlTableModel(this))
{
QVBoxLayout *layout = new QVBoxLayout(this);
m_tableView = new QTableView(this);
layout->addWidget(m_tableView);
QPushButton *insertButton = new QPushButton("新增", this);
QPushButton *deleteButton = new QPushButton("删除", this);
QPushButton *updateButton = new QPushButton("修改", this);
QPushButton *queryButton = new QPushButton("查询", this);
QHBoxLayout *buttonLayout = new QHBoxLayout();
buttonLayout->addWidget(insertButton);
buttonLayout->addWidget(deleteButton);
buttonLayout->addWidget(updateButton);
buttonLayout->addWidget(queryButton);
layout->addLayout(buttonLayout);
m_nameEdit = new QLineEdit(this);
m_ageEdit = new QLineEdit(this);
m_genderCombo = new QComboBox(this);
m_genderCombo->addItem("男");
m_genderCombo->addItem("女");
QHBoxLayout *inputLayout = new QHBoxLayout();
inputLayout->addWidget(m_nameEdit);
inputLayout->addWidget(m_ageEdit);
inputLayout->addWidget(m_genderCombo);
layout->addLayout(inputLayout);
connect(insertButton, &QPushButton::clicked, [this]() {
QString name = m_nameEdit->text();
int age = m_ageEdit->text().toInt();
QString gender = m_genderCombo->currentText();
QSqlQuery query;
query.prepare("INSERT INTO students (name, age, gender) VALUES (:name, :age, :gender)");
query.bindValue(":name", name);
query.bindValue(":age", age);
query.bindValue(":gender", gender);
if (!query.exec()) {
QMessageBox::warning(this, "Error", "Failed to insert record.");
} else {
m_model->select();
}
});
connect(deleteButton, &QPushButton::clicked, [this]() {
QModelIndex index = m_tableView->currentIndex();
if (index.isValid()) {
m_model->removeRow(index.row());
m_model->submitAll();
}
});
connect(updateButton, &QPushButton::clicked, [this]() {
QModelIndex index = m_tableView->currentIndex();
if (index.isValid()) {
QString name = m_nameEdit->text();
int age = m_ageEdit->text().toInt();
QString gender = m_genderCombo->currentText();
m_model->setData(m_model->index(index.row(), 1), name);
m_model->setData(m_model->index(index.row(), 2), age);
m_model->setData(m_model->index(index.row(), 3), gender);
m_model->submitAll();
}
});
connect(queryButton, &QPushButton::clicked, [this]() {
QString name = m_nameEdit->text();
if (!name.isEmpty()) {
m_queryModel->setQuery(QString("SELECT * FROM students WHERE name='%1'").arg(name));
setHeadersForQueryModel(m_queryModel);
m_tableView->setModel(m_queryModel);
} else {
m_model->select();
m_tableView->setModel(m_model);
}
});
// Set headers for the table model
m_model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
m_model->setHeaderData(1, Qt::Horizontal, QObject::tr("姓名"));
m_model->setHeaderData(2, Qt::Horizontal, QObject::tr("年龄"));
m_model->setHeaderData(3, Qt::Horizontal, QObject::tr("性别"));
m_queryModel = new QSqlQueryModel(this);
m_model->setTable("students");
m_model->select();
m_tableView->setModel(m_model);
}
MainWindow::~MainWindow()
{
delete m_model;
delete m_queryModel;
delete m_tableView;
delete m_nameEdit;
delete m_ageEdit;
delete m_genderCombo;
}
void MainWindow::setHeadersForQueryModel(QSqlQueryModel *queryModel)
{
queryModel->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
queryModel->setHeaderData(1, Qt::Horizontal, QObject::tr("姓名"));
queryModel->setHeaderData(2, Qt::Horizontal, QObject::tr("年龄"));
queryModel->setHeaderData(3, Qt::Horizontal, QObject::tr("性别"));
}
3.main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DatabaseManager::getInstance()->getDatabase();
MainWindow w;
w.show();
return a.exec();
}
六、常见问题与优化建议
1. 连接管理:
- 使用QSqlDatabase::contains()检查连接是否已存在,避免重复创建。
- 多线程环境下需为每个线程创建独立连接。
2. 性能优化:
- 批量操作时启用事务,减少磁盘I/O次数。
- 使用索引加速查询,避免全表扫描。
更多Qt开发实战持续更新中。