You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt Sqlite3事务的正确使用方法(Visual Studio开发场景)

问题

使用Visual Studio 2022开发Qt应用时,调用Sqlite3事务出现异常:事务执行过程中,即便某条SQL语句执行失败,之前的操作仍会被提交到数据库,无法触发整体回滚;但在Navicat中执行相同逻辑的SQL事务时,所有操作会正常回滚。请问Qt中正确使用Sqlite3事务的方法是什么?

问题代码片段

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>

int main(int argc, char* argv[])
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("dst.db");
    if (!db.open()) {
        //can't open db
        return 1;
    }

    QSqlQuery* query1 = new QSqlQuery(db);

    db.transaction();

    if (!query1->exec("create table test(u,p);"))
    {
        qDebug() << "create table [fail]\n";
    }

    if (!query1->exec("INSERT INTO test(u,p) VALUES ('tangying','332233');"))
    {
        qDebug() << "insert data [fail]\n";
    }

    //the result of this query should be failed or the result or committing should be failed
    // because the table `tes` not exist
    // the correct result should be that all work should rollback
    //but the previous query execute successfully in the database
    if (!query1->exec("INSERT INTO tes(u,p) VALUES ('tangying','332233');"))
    {
        qDebug() << "insert data2 [fail]\n";
    }

    if (!db.commit())
    {
        qDebug() << "commit [fail]";
    }

    return 0;
}

执行的SQL代码:

BEGIN TRANSACTION;
create table test(u,p);
INSERT INTO test(u,p) VALUES ('tangying','332233');
INSERT INTO tes(u,p) VALUES ('tangying','332233');
COMMIT;

执行结果:

BEGIN TRANSACTION;     OK
create table test(u,p);       OK
INSERT INTO test(u,p) VALUES ('tangying','332233');      Affected rows:1
INSERT INTO tes(u,p) VALUES ('tangying','332233');       No such table

刷新表列表后,所有操作均未执行,符合事务回滚预期。


解决方案

问题核心在于你的代码未在SQL执行失败时主动触发回滚:Qt不会自动在单条SQL执行失败时终止事务,若继续执行后续逻辑并最终调用commit(),之前成功的操作会被提交。正确的做法是在任意一步失败时立即回滚事务并终止流程。

正确事务处理逻辑

  • 启动事务时检查返回值,确保事务成功开启
  • 每一步SQL执行失败时,立即调用db.rollback()回滚所有操作,停止后续流程
  • 所有SQL执行成功后,再调用db.commit()提交事务
  • 使用局部QSqlQuery对象,避免动态分配导致的内存泄漏

修改后的代码示例

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>

int main(int argc, char* argv[])
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("dst.db");
    if (!db.open()) {
        qDebug() << "无法打开数据库";
        return 1;
    }

    QSqlQuery query(db); // 局部对象,自动管理内存

    // 启动事务并检查结果
    if (!db.transaction()) {
        qDebug() << "启动事务失败:" << db.lastError().text();
        db.close();
        return 1;
    }

    // 执行建表语句
    if (!query.exec("create table test(u,p);")) {
        qDebug() << "create table [fail]:" << query.lastError().text();
        db.rollback(); // 失败则回滚
        db.close();
        return 1;
    }

    // 执行插入语句
    if (!query.exec("INSERT INTO test(u,p) VALUES ('tangying','332233');")) {
        qDebug() << "insert data [fail]:" << query.lastError().text();
        db.rollback(); // 失败则回滚
        db.close();
        return 1;
    }

    // 执行错误的插入语句(表名错误)
    if (!query.exec("INSERT INTO tes(u,p) VALUES ('tangying','332233');")) {
        qDebug() << "insert data2 [fail]:" << query.lastError().text();
        db.rollback(); // 失败则回滚
        db.close();
        return 1;
    }

    // 所有操作成功,提交事务
    if (db.commit()) {
        qDebug() << "事务提交成功";
    } else {
        qDebug() << "commit [fail]:" << db.lastError().text();
        db.rollback(); // 提交失败也需回滚
    }

    db.close();
    return 0;
}

关键要点说明

  1. 主动回滚:任何一步SQL执行失败后必须立即调用db.rollback(),否则之前的操作会在commit()时被提交。
  2. 事务启动校验:db.transaction()可能启动失败,必须检查返回值。
  3. 提交失败处理:即使所有SQL执行成功,commit()仍可能因数据库异常失败,此时也需要回滚。
  4. 资源管理:避免使用new QSqlQuery,改用局部对象自动释放内存。

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:38:07