涉及到的库

QSqlDatabase 数据库连接管理

负责管理应用程序与物理数据库之间的连接实例;

  • 选择底层驱动:通过 ​QSqlDatabase::addDatabase("QSQLITE")​ 指定使用 SQLite 引擎。

  • 配置连接参数:通过 ​setDatabaseName()​ 设置本地数据库文件路径(或 ​:memory:​ 内存模式)。

  • 开启/关闭连接:提供 ​open()​ 和 ​close()​ 方法。

  • 事务控制:提供 ​transaction()​(开启事务)、​commit()​(提交)、​rollback()​(回滚)接口,用于提升批量写入性能和保障原子性。

QSqlQuery 执行sql语句 和结果集游标

用于编写、执行 SQL 命令,并遍历执行结果。

  • 执行原生 SQL:直接调用 ​exec("CREATE TABLE ...")​。

  • 预编译与参数化防注入:通过 ​prepare()​ 配合 ​bindValue()​ 绑定占位符(如 ​:name​ 或 ​?​),避免 SQL 注入风险。

  • 遍历查询结果:通过 ​next()​ 向后移动游标读取下一行数据,并用 ​value("column_name")​ 提取具体字段的值。

QSqlError 数据库错误信息的包装

封装数据库层和 Qt 驱动层产生的报错信息

  • 当 ​db.open()​ 或 ​query.exec()​ 返回 ​false​ 时,通过 ​db.lastError()​ 或 ​query.lastError()​ 获取该对象。

  • 调用 ​.text()​、​.databaseText()​ 或 ​.type()​ 提取详细的错误描述(如语法错误、磁盘锁住、字段不存在等),用于排查故障。

简单示例

需要在​CMakeLists.txt​中引入​sql​组件并链接:

find_package(Qt6 REQUIRED COMPONENTS Core Sql)

target_link_libraries(project_name PRIVATE
    Qt6::Core
    Qt6::Sql
)

初始化并建立数据库连接

bool initDatabase() {
    QSqlDatabase db;

    if (QSqlDatabase::contains(QSqlDatabase::defaultConnection)) {
        db = QSqlDatabase::database(QSqlDatabase::defaultConnection);
        if (db.isOpen()) {
            return true;
        }
    } else {
        db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("app_data.db");
    }

    if (!db.open()) {
        qWarning() << "无法打开数据库:" << db.lastError().text();
        return false;
    }
    return true;
}

QSqlDatabase::addDatabase()​ 实际上有两个参数,完整的函数声明是:

QSqlDatabase QSqlDatabase::addDatabase(const QString &type, 
                                       const QString &connectionName = QLatin1String(defaultConnection));

上面的​initDatabase​函数只传递了​"QSQLITE"​,没有第二个参数​connectionName​时,QT会自动注册为默认连接​QSqlDatabase::defaultConnection​,其实就是个特殊字符串​qt_sql_default_connection​;连接会自动缓存在QT的全局静态映射表里,直到程序退出或主动销毁连接;

创建数据库表

bool createTable() {
    QSqlQuery query;
    QString sql = R"(
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            age INTEGER
        )
    )";

    if (!query.exec(sql)) {
        qWarning() << "建表失败:" << query.lastError().text();
        return false;
    }
    return true;
}

这里的​QSqlQuery​同样有一个带默认值的参数:

QSqlQuery(QSqlDatabase db);
QSqlQuery(const QString &query = QString(), QSqlDatabase db = QSqlDatabase());

无参构造 QSqlQuery query; 会使用 Qt 的默认数据库连接;如果默认连接不存在,则这个 query 实际不可用,exec() 会失败。

CRUD

// 3. 插入数据(使用预编译与参数绑定,防止 SQL 注入)
bool insertUser(const QString &name, int age) {
    QSqlQuery query;
    query.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
    query.bindValue(":name", name);
    query.bindValue(":age", age);

    if (!query.exec()) {
        qWarning() << "插入数据失败:" << query.lastError().text();
        return false;
    }
    return true;
}

// 4. 查询数据
void queryUsers() {
    QSqlQuery query("SELECT id, name, age FROM users");

    while (query.next()) {
        int id = query.value("id").toInt();
        QString name = query.value("name").toString();
        int age = query.value("age").toInt();

        qDebug() << "ID:" << id << "| 姓名:" << name << "| 年龄:" << age;
    }
}

// 5. 更新与删除
bool updateUser(int id, int newAge) {
    QSqlQuery query;
    query.prepare("UPDATE users SET age = :age WHERE id = :id");
    query.bindValue(":age", newAge);
    query.bindValue(":id", id);
    return query.exec();
}

bool deleteUser(int id) {
    QSqlQuery query;
    query.prepare("DELETE FROM users WHERE id = :id");
    query.bindValue(":id", id);
    return query.exec();
}

需要连接多个数据库

如果要连接多个数据库,如一个本地Sqlite,一个远程Mysql时,就不能依赖QT的默认连接,需要显示命名:

// 1. 创建两个命名连接
QSqlDatabase localDb = QSqlDatabase::addDatabase("QSQLITE", "local_conn");
localDb.setDatabaseName("local.db");
localDb.open();

QSqlDatabase remoteDb = QSqlDatabase::addDatabase("QMYSQL", "remote_conn");
remoteDb.setHostName("192.168.1.100");
// ... 设置并打开远程连接

// 2. 指定给 QSqlQuery 使用
QSqlQuery query1(localDb); // 指向本地数据库
query1.exec("SELECT * FROM local_table");

// 或者通过名字查找已存在的连接传进去:
QSqlQuery query2(QSqlDatabase::database("remote_conn")); // 指向远程数据库
query2.exec("SELECT * FROM remote_table");

一些注意事项

因为 Qt 在后台用全局池管理连接,如果作用域管理不当,很容易触发警告:

QSqlDatabasePrivate::removeDatabase: connection 'xxx' is still in use, all queries will cease to work.

原因​:

如果在同一个函数内,​QSqlDatabase​ 和 ​QSqlQuery​ 混在一起,可能导致 ​QSqlQuery​ 还没析构,连接就尝试关闭或移除。

规范写法​:

让 ​QSqlQuery​ 尽量保持较小的局部生命周期,例如在函数内部创建 ​QSqlQuery query;​,执行完随函数返回立即析构,不要把 ​QSqlQuery​ 存为长期的成员变量,这样最安全。