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

Sqlite3 C++数据库锁定问题:修改记录时出现database is locked错误

解决SQLite "database is locked" 错误:未正确关闭数据库连接的问题

嘿,我一眼就揪出问题所在了——你的checkAvailability方法里有个典型的逻辑错误:return语句写在了资源释放代码的前面,导致数据库连接根本没被正常关闭,直接把数据库锁住了!

咱们仔细看这段出问题的代码:

if (sqlite3_step (stmt) == SQLITE_ROW) {
    return true;
    sqlite3_finalize (stmt);
    sqlite3_close (db);
} else {
    return false;
    sqlite3_finalize (stmt);
    sqlite3_close (db);
}

一旦执行return true或者return false,函数会立刻结束,后面的sqlite3_finalize(释放预处理语句)和sqlite3_close(关闭数据库连接)根本不会运行!这就意味着数据库连接一直处于打开状态,后续调用updateRecord尝试再次打开同一个数据库时,SQLite就会抛出"database is locked"错误。

另外,你在modifyBookRecords里还多调用了一次database.closeDb()——这其实是画蛇添足,而且如果checkAvailability已经出问题没关闭连接,这行代码也救不了,反而可能导致重复关闭的额外问题。

修复方案

首先,调整checkAvailability的代码顺序,确保不管查询结果如何,都先释放资源、关闭数据库,再返回结果:

bool Database::checkAvailability(std::string& input) {
    openDb(dir);
    sqlite3_stmt* stmt = nullptr;
    rc = sqlite3_prepare_v2(db, "SELECT * FROM Books WHERE Title = ? OR Isbn = ?", -1, &stmt, NULL);
    
    if (rc != SQLITE_OK) {
        std::cout << sqlite3_errmsg(db);
        sqlite3_close(db); // 出错时也要确保关闭数据库
        return false;
    }

    sqlite3_bind_text(stmt, 1, input.c_str(), input.length(), NULL);
    sqlite3_bind_text(stmt, 2, input.c_str(), input.length(), NULL);
    
    // 先把查询结果存起来
    bool recordExists = (sqlite3_step(stmt) == SQLITE_ROW);
    
    // 必须先释放预处理语句,再关闭数据库
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    
    return recordExists;
}

然后,修改modifyBookRecords,去掉那个多余的closeDb调用:

void Bookshop::modifyBookRecords () {
    std::cout << "You want to modify a book" << std::endl;
    std::cout << "Title/ISBN: ";
    std::string input;
    std::getline (std::cin, input);
    
    bool isAvailable = database.checkAvailability (input);
    // 删掉这行!checkAvailability内部已经关闭了数据库
    // database.closeDb ();
    
    if (isAvailable) {
        std::cout << "Title: ";
        std::string title;
        std::getline (std::cin, title);
        std::cout << "Author: ";
        std::string author;
        std::getline (std::cin, author);
        std::cout << "Pages: ";
        int pages;
        std::cin >> pages;
        std::cin.ignore (std::numeric_limits<std::streamsize>::max (), '\n');
        std::cout << "Genre: ";
        std::string genre;
        std::getline (std::cin, genre);
        std::cout << "Publisher: ";
        std::string publisher;
        std::getline (std::cin, publisher);
        std::cout << "ISBN: ";
        std::string isbn;
        std::getline (std::cin, isbn);
        std::cout << "Number of Copies: ";
        int numberOfCopies;
        std::cin >> numberOfCopies;
        std::cin.ignore (std::numeric_limits<std::streamsize>::max (), '\n');
        
        database.updateRecord (input, title, author, pages, genre, publisher, isbn, numberOfCopies);
    } else {
        std::cout << "The book was not found" << std::endl;
    }
}

额外建议

为了避免以后再犯类似的错误,你可以考虑用**RAII(资源获取即初始化)**的方式来管理SQLite的连接和预处理语句——比如写个简单的封装类,在构造函数里打开连接/创建stmt,析构函数里自动释放资源,这样就不用手动记着调用close和finalize了,能大幅减少这类资源泄漏的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 18:42:36