数据持久化如何优雅实现?深入Qt数据库模块,构建高性能、可维护的数据访问层
一、Qt SQL模块概述
Qt提供了完整的数据库访问框架,支持多种数据库后端,提供底层SQL操作和高层ORM抽象。
1.1 支持的数据库驱动
| 驱动名称 | 数据库 | 特点 |
|---|---|---|
| QSQLITE | SQLite | 嵌入式,无需服务器 |
| QMYSQL | MySQL/MariaDB | 开源,广泛使用 |
| QPSQL | PostgreSQL | 功能强大,开源 |
| QOCI | Oracle | 企业级数据库 |
| QODBC | ODBC | 通用接口 |
| QDB2 | DB2 | IBM数据库 |
1.2 核心类结构
cpp
#include <QSqlDatabase> // 数据库连接
#include <QSqlQuery> // SQL查询
#include <QSqlError> // 错误处理
#include <QSqlRecord> // 记录抽象
#include <QSqlField> // 字段抽象
#include <QSqlTableModel> // 表格模型
#include <QSqlRelationalTableModel> // 关系模型
1.3 源码位置
qtbase/src/sql/
├── kernel/
│ ├── qsqldatabase.cpp/h
│ ├── qsqlquery.cpp/h
│ └── qsqlerror.cpp/h
├── models/
│ ├── qsqltablemodel.cpp/h
│ └── qsqlrelationaltablemodel.cpp/h
└── drivers/
├── sqlite/
├── mysql/
└── psql/
二、数据库连接管理
2.1 SQLite连接
cpp
#include <QSqlDatabase>
#include <QSqlError>
bool connectSQLite(const QString &path)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(path);
if (!db.open()) {
qCritical() << "打开数据库失败:" << db.lastError().text();
return false;
}
// 启用外键约束
QSqlQuery query;
query.exec("PRAGMA foreign_keys = ON");
// 设置WAL模式提高并发性能
query.exec("PRAGMA journal_mode = WAL");
return true;
}
2.2 MySQL连接
cpp
bool connectMySQL(const QString &host, const QString &dbName,
const QString &user, const QString &password)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName(host);
db.setDatabaseName(dbName);
db.setUserName(user);
db.setPassword(password);
db.setPort(3306);
// 连接选项
db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=5");
if (!db.open()) {
qCritical() << "MySQL连接失败:" << db.lastError().text();
return false;
}
// 设置字符集
QSqlQuery query;
query.exec("SET NAMES utf8mb4");
return true;
}
2.3 连接池管理
cpp
// DatabasePool.h
class DatabasePool : public QObject
{
Q_OBJECT
public:
static DatabasePool* instance();
QSqlDatabase getConnection();
void returnConnection(QSqlDatabase db);
void setMaxConnections(int max) { m_maxConnections = max; }
void setIdleTimeout(int ms) { m_idleTimeout = ms; }
private:
DatabasePool();
QSqlDatabase createConnection();
void cleanupIdleConnections();
QList<QSqlDatabase> m_availableConnections;
QSet<QString> m_usedConnections;
int m_maxConnections = 10;
int m_idleTimeout = 300000; // 5分钟
QMutex m_mutex;
QTimer m_cleanupTimer;
};
// DatabasePool.cpp
QSqlDatabase DatabasePool::getConnection()
{
QMutexLocker locker(&m_mutex);
// 尝试复用空闲连接
if (!m_availableConnections.isEmpty()) {
QSqlDatabase db = m_availableConnections.takeFirst();
if (db.isOpen()) {
m_usedConnections.insert(db.connectionName());
return db;
}
}
// 创建新连接
if (m_usedConnections.size() < m_maxConnections) {
QSqlDatabase db = createConnection();
if (db.isOpen()) {
m_usedConnections.insert(db.connectionName());
return db;
}
}
return QSqlDatabase(); // 返回空连接
}
void DatabasePool::returnConnection(QSqlDatabase db)
{
QMutexLocker locker(&m_mutex);
QString connName = db.connectionName();
if (m_usedConnections.contains(connName)) {
m_usedConnections.remove(connName);
m_availableConnections.append(db);
}
}
三、SQL查询操作
3.1 基本查询
cpp
#include <QSqlQuery>
#include <QSqlRecord>
// 执行查询
void executeQuery()
{
QSqlQuery query;
// 执行SQL语句
if (!query.exec("SELECT * FROM users WHERE age > 18")) {
qWarning() << "查询失败:" << query.lastError().text();
return;
}
// 遍历结果
while (query.next()) {
int id = query.value(0).toInt();
QString name = query.value(1).toString();
int age = query.value("age").toInt(); // 按字段名
qDebug() << id << name << age;
}
}
// 参数化查询
void parameterizedQuery()
{
QSqlQuery query;
query.prepare("SELECT * FROM users WHERE name = :name AND age > :age");
query.bindValue(":name", "张三");
query.bindValue(":age", 18);
if (query.exec()) {
while (query.next()) {
// 处理结果
}
}
}
3.2 插入操作
cpp
// 插入数据
bool insertUser(const QString &name, int age, const QString &email)
{
QSqlQuery query;
query.prepare("INSERT INTO users (name, age, email) VALUES (?, ?, ?)");
query.addBindValue(name);
query.addBindValue(age);
query.addBindValue(email);
if (!query.exec()) {
qWarning() << "插入失败:" << query.lastError().text();
return false;
}
// 获取自增ID
qint64 id = query.lastInsertId().toLongLong();
qDebug() << "插入成功,ID:" << id;
return true;
}
// 批量插入
bool batchInsertUsers(const QList<User> &users)
{
QSqlDatabase db = QSqlDatabase::database();
db.transaction();
QSqlQuery query;
query.prepare("INSERT INTO users (name, age, email) VALUES (?, ?, ?)");
for (const User &user : users) {
query.addBindValue(user.name);
query.addBindValue(user.age);
query.addBindValue(user.email);
query.exec();
}
return db.commit();
}
3.3 更新和删除
cpp
// 更新数据
bool updateUserAge(qint64 userId, int newAge)
{
QSqlQuery query;
query.prepare("UPDATE users SET age = ? WHERE id = ?");
query.addBindValue(newAge);
query.addBindValue(userId);
if (!query.exec()) {
return false;
}
return query.numRowsAffected() > 0;
}
// 删除数据
bool deleteUser(qint64 userId)
{
QSqlQuery query;
query.prepare("DELETE FROM users WHERE id = ?");
query.addBindValue(userId);
if (!query.exec()) {
return false;
}
return query.numRowsAffected() > 0;
}
四、事务处理
4.1 手动事务
cpp
bool transferMoney(qint64 fromId, qint64 toId, double amount)
{
QSqlDatabase db = QSqlDatabase::database();
if (!db.transaction()) {
qCritical() << "开启事务失败";
return false;
}
QSqlQuery query;
try {
// 检查余额
query.prepare("SELECT balance FROM accounts WHERE id = ?");
query.addBindValue(fromId);
if (!query.exec() || !query.next()) {
throw std::runtime_error("查询余额失败");
}
double balance = query.value(0).toDouble();
if (balance < amount) {
throw std::runtime_error("余额不足");
}
// 扣款
query.prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
query.addBindValue(amount);
query.addBindValue(fromId);
if (!query.exec()) {
throw std::runtime_error("扣款失败");
}
// 入账
query.prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
query.addBindValue(amount);
query.addBindValue(toId);
if (!query.exec()) {
throw std::runtime_error("入账失败");
}
// 提交事务
if (!db.commit()) {
throw std::runtime_error("提交事务失败");
}
return true;
} catch (const std::exception &e) {
db.rollback();
qCritical() << "转账失败:" << e.what();
return false;
}
}
4.2 RAII事务包装
cpp
class TransactionGuard
{
public:
explicit TransactionGuard(QSqlDatabase db)
: m_db(db), m_committed(false)
{
m_db.transaction();
}
~TransactionGuard()
{
if (!m_committed) {
m_db.rollback();
}
}
bool commit()
{
if (m_db.commit()) {
m_committed = true;
return true;
}
return false;
}
private:
QSqlDatabase m_db;
bool m_committed;
};
// 使用
void someOperation()
{
TransactionGuard guard(QSqlDatabase::database());
// 执行多个数据库操作
// ...
if (success) {
guard.commit();
}
// 自动回滚如果未提交
}
五、Model-View数据库展示
5.1 QSqlTableModel
cpp
#include <QSqlTableModel>
#include <QTableView>
void setupTableView()
{
QSqlTableModel *model = new QSqlTableModel();
model->setTable("users");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
// 设置表头
model->setHeaderData(0, Qt::Horizontal, "ID");
model->setHeaderData(1, Qt::Horizontal, "姓名");
model->setHeaderData(2, Qt::Horizontal, "年龄");
// 绑定到视图
QTableView *view = new QTableView();
view->setModel(model);
view->show();
// 提交修改
model->submitAll();
// 撤销修改
model->revertAll();
}
5.2 QSqlRelationalTableModel
cpp
// 关系型表格模型
void setupRelationalModel()
{
QSqlRelationalTableModel *model = new QSqlRelationalTableModel();
model->setTable("orders");
// 设置关系:orders.user_id -> users.id
model->setRelation(1, QSqlRelation("users", "id", "name"));
model->select();
QTableView *view = new QTableView();
view->setModel(model);
view->setItemDelegate(new QSqlRelationalDelegate(view));
}
六、ORM架构设计
6.1 基础ORM框架
cpp
// Entity.h - 实体基类
class Entity
{
public:
virtual ~Entity() = default;
virtual QString tableName() const = 0;
virtual QStringList columnNames() const = 0;
virtual QVariantMap toMap() const = 0;
virtual void fromMap(const QVariantMap &map) = 0;
qint64 id() const { return m_id; }
void setId(qint64 id) { m_id = id; }
bool save();
bool remove();
static QVariantList find(const QString &where = QString(),
const QVariantList &bindValues = QVariantList());
protected:
qint64 m_id = 0;
};
// Entity.cpp
bool Entity::save()
{
QSqlQuery query;
if (m_id == 0) {
// 插入
QStringList cols = columnNames();
QStringList placeholders;
for (int i = 0; i < cols.size(); ++i) {
placeholders << "?";
}
QString sql = QString("INSERT INTO %1 (%2) VALUES (%3)")
.arg(tableName())
.arg(cols.join(", "))
.arg(placeholders.join(", "));
query.prepare(sql);
QVariantMap data = toMap();
for (const QString &col : cols) {
query.addBindValue(data[col]);
}
if (query.exec()) {
m_id = query.lastInsertId().toLongLong();
return true;
}
} else {
// 更新
QStringList cols = columnNames();
QStringList setClauses;
for (const QString &col : cols) {
setClauses << QString("%1 = ?").arg(col);
}
QString sql = QString("UPDATE %1 SET %2 WHERE id = ?")
.arg(tableName())
.arg(setClauses.join(", "));
query.prepare(sql);
QVariantMap data = toMap();
for (const QString &col : cols) {
query.addBindValue(data[col]);
}
query.addBindValue(m_id);
return query.exec();
}
return false;
}
6.2 实体类示例
cpp
// User.h
class User : public Entity
{
public:
User() = default;
QString tableName() const override { return "users"; }
QStringList columnNames() const override {
return {"name", "age", "email", "created_at"};
}
QVariantMap toMap() const override {
return {
{"name", m_name},
{"age", m_age},
{"email", m_email},
{"created_at", m_createdAt}
};
}
void fromMap(const QVariantMap &map) override {
m_id = map["id"].toLongLong();
m_name = map["name"].toString();
m_age = map["age"].toInt();
m_email = map["email"].toString();
m_createdAt = map["created_at"].toDateTime();
}
// 属性
QString name() const { return m_name; }
void setName(const QString &name) { m_name = name; }
int age() const { return m_age; }
void setAge(int age) { m_age = age; }
QString email() const { return m_email; }
void setEmail(const QString &email) { m_email = email; }
private:
QString m_name;
int m_age = 0;
QString m_email;
QDateTime m_createdAt;
};
// 使用
User user;
user.setName("张三");
user.setAge(28);
user.setEmail("zhangsan@example.com");
user.save(); // 插入数据库
user.setAge(29);
user.save(); // 更新数据库
七、性能优化
7.1 查询优化
cpp
// 只查询需要的列
QSqlQuery query;
query.prepare("SELECT id, name FROM users WHERE ..."); // 而不是 SELECT *
// 使用索引
query.exec("CREATE INDEX idx_users_name ON users(name)");
// 分页查询
int page = 1;
int pageSize = 20;
int offset = (page - 1) * pageSize;
query.prepare("SELECT * FROM users LIMIT ? OFFSET ?");
query.addBindValue(pageSize);
query.addBindValue(offset);
7.2 批量操作优化
cpp
// 使用批量插入
QSqlQuery query;
query.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
QVariantList names = {"张三", "李四", "王五"};
QVariantList ages = {28, 25, 30};
query.addBindValue(names);
query.addBindValue(ages);
query.execBatch(); // 批量执行
八、总结
Qt数据库编程核心要点:
- 连接管理:QSqlDatabase + 连接池
- SQL操作:QSqlQuery + 参数绑定
- 事务处理:transaction/commit/rollback
- Model-View:QSqlTableModel/QSqlRelationalTableModel
- ORM设计:Entity抽象 + 映射机制
- 性能优化:索引、分页、批量操作
《注:若有发现问题欢迎大家提出来纠正》