Qt数据库编程深度解析:从SQL基础到ORM架构设计

数据持久化如何优雅实现?深入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数据库编程核心要点:

  1. 连接管理:QSqlDatabase + 连接池
  2. SQL操作:QSqlQuery + 参数绑定
  3. 事务处理:transaction/commit/rollback
  4. Model-View:QSqlTableModel/QSqlRelationalTableModel
  5. ORM设计:Entity抽象 + 映射机制
  6. 性能优化:索引、分页、批量操作

《注:若有发现问题欢迎大家提出来纠正》

相关推荐
CSCN新手听安2 小时前
【Qt】Qt窗口(六)QMessageBox消息对话框的使用
开发语言·c++·qt
Database_Cool_2 小时前
在 RDS PostgreSQL 中实现 RaBitQ 量化
数据库·阿里云·ai·postgresql
【心态好不摆烂】2 小时前
MySQL操作库
数据库·mysql
Javatutouhouduan2 小时前
Java小白如何快速玩转Redis?
java·数据库·redis·分布式锁·java面试·后端开发·java程序员
爱看书的小沐2 小时前
【小沐学WebGIS】基于Cesium.JS与jsbsim联动三维飞行仿真(OpenGL、Cesium.js、Three.js)
c++·qt·three.js·opengl·cesium·jsbsim
Lyyaoo.3 小时前
Redisson
数据库·缓存
网络工程小王4 小时前
【LCEL 链式调用详解】调用篇-2
java·服务器·前端·数据库·人工智能
道法自然,人法天4 小时前
PostgreSQL安装与初始化教程(二进制压缩包)
数据库·postgresql
yzs875 小时前
从Hydra到storage_engine:PostgreSQL列存引擎的性能跃迁与技术进化
数据库·postgresql