资讯动态

SQLiteCpp 教程:C++11 封装 SQLite3 从零实战,5 个场景带你吃透查询、注入防护与事务

发布时间:2026/8/19 13:09:18 来源:尧图企业网站定制
SQLiteCpp 教程C11 封装 SQLite3 从零实战5 个场景带你吃透查询、注入防护与事务【免费下载链接】SQLiteCppSQLiteC (SQLiteCpp) is a smart and easy to use C SQLite3 wrapper.项目地址: https://gitcode.com/gh_mirrors/sq/SQLiteCppSQLiteCpp 是一个用现代 C11 编写的轻量级 SQLite3 封装库它把原生 C API 收进几个直观的类里配合 RAII 资源管理与异常处理让数据库操作既安全又省心。这篇 SQLiteCpp 教程会从一个真实困扰出发带你走完认识、集成、实战、避坑、进阶的完整路径全程代码可直接复制运行。一个 C 开发者的烦恼恰好是 SQLiteCpp 的起点假设你在写一个桌面端的小工具需要保存订单、记录库存、缓存用户配置。SQLite 几乎是天然答案——单文件、免安装、零服务端但当你打开sqlite3.h迎面而来的是上百个 C 函数sqlite3_open_v2、sqlite3_prepare_v2、sqlite3_bind_int、sqlite3_step、sqlite3_column_text、sqlite3_finalize……真正的痛点还不只是函数多而是资源管理每次查询都要记住在正确时机调用sqlite3_finalize、sqlite3_close一旦中途抛了异常很容易漏掉某个释放调用内存泄漏就悄悄埋下了。SQLiteCpp 想解决的正是这件事用 C 的方式包装 SQLite让你像操作普通对象一样操作数据库把容易忘的收尾工作全部交给析构函数。它诞生于 2012 年至今仍在活跃维护源码就放在include/SQLiteCpp/与src/两个目录下。SQLiteCpp 是什么把几百个 C 函数装进几个类里先花一分钟认识它的班底。整个库的核心只有几个类各自只负责一件事类职责SQLite::Database数据库连接打开文件、建表、执行语句、单值查询SQLite::Statement预处理语句绑定参数、逐行取数、重置复用SQLite::Column结果集中的一列自动类型转换、文本/BLOB 读取SQLite::Transaction事务提交、异常时自动回滚SQLite::Backup数据库在线备份SQLite::Savepoint事务内的保存点支持局部回滚SQLite::Exception错误异常类型继承自std::runtime_error支撑这些类的设计理念是三条简单到可以背下来的原则RAII 贯穿始终连接、语句、事务都在构造时取得资源、析构时自动释放。你永远不需要手写close或finalize。出错就抛异常SQLite 返回错误码时库会统一转换为SQLite::Exception抛出错误信息比裸错误码友好得多仅在析构函数里改用断言析构中不能抛异常。只依赖 STL 与 SQLite3C11 标准库加一个 sqlite3 库就是全部家当不引 boost不引其他框架。再加上跨平台Linux / Windows / macOS 均有 CI 持续验证、MIT 许可商用无忧、每个类都有 Doxygen 注释、tests/ 下有完整的 googletest 单元测试——这些特质让它很适合作为项目的持久层基础设施。三步完成 CMake 集成 SQLiteCpp从 clone 到跑通第一个程序把库装进项目比想象中简单。核心就三句话拉代码、add_subdirectory、target_link_libraries。第一步获取源码git clone https://gitcode.com/gh_mirrors/sq/SQLiteCpp.git cd SQLiteCpp git submodule init git submodule update后面两步是为了拉取googletest/子模块只有当你打算编译单元测试时才需要。第二步让 CMake 认识这个库在你的项目根CMakeLists.txt里加两行add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp) add_executable(app src/main.cpp) target_link_libraries(app PRIVATE SQLiteCpp)默认情况下CMake 配置会开启SQLITECPP_INTERNAL_SQLITE直接编译项目自带的sqlite3/目录下的源码所以你不必额外安装 sqlite3 开发包如果希望改用系统库把它关掉并安装libsqlite3-dev即可。如果你把库安装到了系统里也可以改用find_package(SQLiteCpp REQUIRED)的方式链接。第三步跑通第一个最小程序#include SQLiteCpp/SQLiteCpp.h #include iostream int main() { try { SQLite::Database db(shop.db3, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); db.exec(CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, sku TEXT NOT NULL, qty INTEGER NOT NULL, price REAL NOT NULL)); std::cout 数据库就绪 std::endl; } catch (const std::exception e) { std::cerr 数据库错误: e.what() std::endl; return 1; } return 0; }编译、运行能看到数据库就绪说明集成成功接下来就可以进入实战了。实战场景一打开数据库、建表与基础增删改SQLite::Database的构造函数第一个参数是文件路径第二个是可选的打开方式。有几种常用的姿势SQLite::Database db(shop.db3)—— 默认只读打开SQLite::Database db(shop.db3, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)—— 读写文件不存在则创建SQLite::Database db(:memory:, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)—— 纯内存库程序退出数据即消失适合测试。连接建立后exec()可以执行建表、插入、更新等语句并返回受影响的行数int rows db.exec(INSERT INTO orders (sku, qty, price) VALUES (SKU-001, 5, 19.9)); std::cout 插入行数: rows std::endl;两个高频小工具值得记住// 表是否存在常用于启动时做迁移判断 bool ok db.tableExists(orders); // 单值查询的快捷方式取第一行第一列 // 注意必须拷贝到自己的变量里否则临时对象析构后结果就失效了 const std::string sku db.execAndGet(SELECT sku FROM orders WHERE id 1);如果想知道连接期间累计改动了多少行db.getTotalChanges()可以给你答案。这套 API 与 SQLite 原生命名风格保持一致学过 SQLite 的人几乎零成本迁移。实战场景二SQLiteCpp 防 SQL 注入写法——预处理语句与绑定参数直接把用户输入拼进 SQL 字符串是危险的SELECT * FROM orders WHERE sku input 一旦遇到恶意输入轻则查询错乱重则拖库。正确的姿势是预处理语句 参数绑定让 SQLite 引擎替你处理转义。SQLite::Statement stmt(db, SELECT sku, qty, price FROM orders WHERE qty ?); // 位置参数从 1 开始编号 stmt.bind(1, 5); while (stmt.executeStep()) { // 逐行推进结果集 std::string sku stmt.getColumn(0); // Column 支持隐式转换为 C 类型 int qty stmt.getColumn(1); double price stmt.getColumn(2); std::cout sku x qty price std::endl; }几个要点executeStep()每调用一次前进到下一行返回true表示还有数据用它做SELECT查询用exec()做写操作。getColumn(i)返回SQLite::Column它针对int、double、std::string都有隐式转换也可以显式调用.getInt()、.getText()。参数除了用数字下标还支持命名参数。例如 SQL 里写:min_qty代码里就stmt.bind(:min_qty, 5)可读性更好。如果同一句 SQL 要反复执行绑定新值前调用stmt.reset()即可复用不用重新编译语句SQLite::Statement ins(db, INSERT INTO orders (sku, qty, price) VALUES (?, ?, ?)); for (const auto o : orders) { ins.bind(1, o.sku); ins.bind(2, o.qty); ins.bind(3, o.price); ins.exec(); ins.reset(); }实战场景三C SQLite3 事务处理示例——批量入库不再提心吊胆批量写数据时逐条INSERT很慢因为每次写入都隐式开启一个事务。把整批操作包进一个显式事务里速度会有质的提升更重要的是事务能保证要么全成、要么全无。SQLiteCpp 的事务用法相当优雅——构造SQLite::Transaction对象即开启事务try { SQLite::Database db(shop.db3, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); // 构造即 BEGIN SQLite::Transaction tx(db); db.exec(UPDATE inventory SET stock stock - 5 WHERE sku SKU-001); db.exec(UPDATE inventory SET stock stock 5 WHERE sku SKU-002); // 显式提交如果不调用 commit()析构时会自动回滚 tx.commit(); } catch (const std::exception e) { // 事务内任何一步抛出异常事务对象析构时自动 ROLLBACK std::cerr 事务失败: e.what() std::endl; }这段代码最打动人的地方在于你不需要写任何 rollback 代码。只要commit()之前的某一步抛了异常异常向上传播、tx析构回滚就自动发生了。相比手写sqlite3_exec(db, ROLLBACK)的做法少了一整类忘了回滚的 bug。如果你只需要回滚到事务中间某一点可以配合SQLite::Savepoint使用实现嵌套级别的控制。实战场景四BLOB 二进制数据存取把图片、文件塞进数据库SQLite 的 BLOB 类型可以保存任意二进制数据SQLiteCpp 对它的支持同样顺手。官方示例examples/example1/main.cpp里就演示了把一张 PNG 图片存入:memory:数据库再读出来完整代码值得一读。这里给出精简版// 写入把二进制块绑定到参数 std::vectorchar imageData readFile(photo.jpg); SQLite::Statement ins(db, INSERT INTO attachments (name, data) VALUES (?, ?)); ins.bind(1, photo.jpg); ins.bind(2, imageData.data(), static_castint(imageData.size())); ins.exec(); // 读出getBlob 拿到指针getBytes 拿到字节数 SQLite::Statement sel(db, SELECT data FROM attachments WHERE name ?); sel.bind(1, photo.jpg); if (sel.executeStep()) { const void* blob sel.getColumn(0).getBlob(); size_t size sel.getColumn(0).getBytes(); // 拷贝或写入文件…… }有一点要留意getBlob返回的指针指向语句内部缓冲区在Statement析构之前使用是安全的不要长时间保存这个指针。进阶玩法C14 变参绑定与列元数据让代码再薄一层如果你愿意把编译标准切到 C14可以解锁VariadicBind.h里的SQLite::bind把多个bind调用压缩成一行#include SQLiteCpp/VariadicBind.h SQLite::Statement stmt(db, INSERT INTO orders (sku, qty, price) VALUES (?, ?, ?)); SQLite::bind(stmt, SKU-099, 3, 19.9); stmt.exec();参数从左到右依次填充问号类型由编译器自动推导代码更紧凑也更好维护。另一个实用功能是列元数据。在开启SQLITE_ENABLE_COLUMN_METADATA宏的前提下CMake 选项默认开启前提是 sqlite3 库本身也以该宏编译可以同时拿到结果列的别名与真实来源列名SQLite::Statement stmt(db, SELECT sku AS item_code, qty FROM orders); std::string alias stmt.getColumnName(0); // item_codeSELECT 里的别名 std::string origin stmt.getColumnOriginName(0); // sku表里的原始列名这在做 ORM 映射、报表导出时非常有用。项目还提供了ExecuteMany.h批量执行、Backup.h在线备份、Database::getHeaderInfo()读取 db3 文件头部元信息等能力等你按需探索。踩坑记录SQLiteCpp 新手最常问的四个问题1. 断言失败怎么自定义处理析构函数里不能抛异常所以库内部用SQLITECPP_ASSERT()代替。默认行为是走 C 的assert()想自定义处理方式编译时定义SQLITECPP_ENABLE_ASSERT_HANDLER宏并实现SQLite::assertion_failed函数即可参考examples/example1/main.cpp顶部的写法。2. 能多线程使用吗SQLiteCpp 自身不加锁这是刻意为之为了轻量和高性能支持 SQLite 的多线程模式多个线程可以各自使用不同的连接但同一个连接不能同时被两个线程使用。更彻底的Serialized模式它并不支持多线程场景请自行做好连接隔离。3. 如何获取 SQLite 版本SQLite::VERSION是编译期头文件的版本字符串SQLite::getLibVersion()是运行时链接库的版本SQLITECPP_VERSION则是 SQLiteCpp 自身的版本。三者打印出来排错时一眼看出版本差异。4. 链接报undefined reference to sqlite3_column_origin_name这说明你的 sqlite3 库没有开启SQLITE_ENABLE_COLUMN_METADATA。三个解决办法换用项目自带的内部 sqlite3开启SQLITECPP_INTERNAL_SQLITE这是默认值或关闭SQLITE_ENABLE_COLUMN_METADATA选项放弃该功能或自行重新编译系统 sqlite3。Ubuntu 系发行版自带的 sqlite3 通常已开启macOS 上则容易踩这个坑。跑一遍官方示例与单元测试构建命令速查把仓库里的示例和测试跑起来是检验环境、理解 API 的最快方式。CMake 路径mkdir build cd build cmake -DSQLITECPP_BUILD_EXAMPLESON -DSQLITECPP_BUILD_TESTSON .. cmake --build . ctest --output-on-failurectest会运行单元测试如果测试通过环境就没问题。之后可以逐个阅读examples/example1/main.cpp——它一口气演示了查询、别名列名、对象化封装、批量语句、事务、BLOB、变参绑定等 7 个片段堪称最浓缩的教程。偏好 Meson 的话同样支持meson setup builddir -DSQLITECPP_BUILD_TESTStrue -DSQLITECPP_BUILD_EXAMPLEStrue meson compile -C builddir学习地图项目里藏着最好的教程与其到处搜资料不如按下面这条路径把仓库读一遍收益最高官方文档docs/README.md——项目完整说明含平台支持、依赖、FAQ、排错核心头文件include/SQLiteCpp/Database.h、Statement.h、Column.h、Transaction.h每个类都有详尽的 Doxygen 注释读注释就能学会 80% 的用法示例代码examples/example1/main.cpp 与 examples/example2/src/main.cpp——前者覆盖面广后者展示最小工程结构单元测试tests/——测试用例即文档Database_test.cpp、Statement_test.cpp、Transaction_test.cpp等展示了大量边界用法构建配置CMakeLists.txt 与meson_options.txt——所有可调开关示例、测试、列元数据、内部 sqlite都在这里变更历史CHANGELOG.md 与 TODO.txt——了解版本演进和已知规划。写在最后回到开头的那个开发者他需要的其实不是更高级的数据库而是更省心的调用方式。SQLiteCpp 的价值正在于此——它没有发明新概念只是把 SQLite 的 C API 用 C 的语言习惯重新表达了一遍对象管理资源异常传递错误语句复用、事务回滚都变成了一目了然的代码。从今天这篇 SQLiteCpp 教程出发你可以先跑通示例再拿真实业务订单、库存、日志练手最后按需深入源码。SQLite 的可靠 C 的优雅这套组合足够支撑起大多数中小型应用的持久层需求。祝你用得顺手少踩几个坑。【免费下载链接】SQLiteCppSQLiteC (SQLiteCpp) is a smart and easy to use C SQLite3 wrapper.项目地址: https://gitcode.com/gh_mirrors/sq/SQLiteCpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价