ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SQLiteCpp入门指南:如何用现代C++快速驾驭SQLite3数据库

SQLiteCpp入门指南:如何用现代C++快速驾驭SQLite3数据库 SQLiteCpp入门指南如何用现代C快速驾驭SQLite3数据库【免费下载链接】SQLiteCppSQLiteC (SQLiteCpp) is a smart and easy to use C SQLite3 wrapper.项目地址: https://gitcode.com/gh_mirrors/sq/SQLiteCpp你是不是也曾在项目里被 SQLite 的原生 C API 折磨过打开数据库要sqlite3_open执行语句要sqlite3_prepare_v2绑参数要sqlite3_bind_text每走一步还得手动检查返回值是不是SQLITE_OK……写业务逻辑的时间全耗在跟底层 API 较劲上了。SQLiteCpp 就是来解决这个痛点的——它是一个轻量级的 C11 SQLite3 封装库把底层 C 接口包成几个直观的类让数据库操作回归到写业务本身。它是来帮你解决问题的不是来凑热闹的先别急着背 API我们换个思路SQLiteCpp 到底在什么场景下真正值得用你的项目想用 SQLite但不想跟 C 接口纠缠封装之后建表、查询、插入都只是几行普通 C 代码。你受够了手动管理资源sqlite3_finalize忘调、连接忘关、异常路径资源泄漏……这些在 SQLiteCpp 里基本绝迹因为整个库建立在 RAII 之上。你可以把 RAII 想象成酒店房间入住时自动开锁退房时自动锁门哪怕中途出了岔子抛出异常门也不会被落下。你希望代码既安全又好看预处理语句天然防 SQL 注入异常机制把错误信息带得明明白白不用再靠if (rc ! SQLITE_OK)的瀑布流撑场面。简单说凡是C SQLite3的组合SQLiteCpp 都能让你写得更快、更稳、更体面。它只依赖 C11 STL 和 SQLite3 本身MIT 协议商用无压力跨 Linux / Windows / macOS 三平台——是个很省心的选择。十分钟跑起来安装与最小可运行示例第一步把源码拿到手克隆仓库内含 sqlite3 与 googletest 子模块记得同步一下git clone https://gitcode.com/gh_mirrors/sq/SQLiteCpp cd SQLiteCpp git submodule init git submodule update第二步接入你的 CMake 工程在项目根 CMakeLists.txt 里加上这一小段把 SQLiteCpp 当子目录引入即可add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/thirdparty/SQLiteCpp) add_executable(my_app src/main.cpp) target_link_libraries(my_app SQLiteCpp sqlite3 pthread dl )第三步写下第一个能跑的代码#include SQLiteCpp/SQLiteCpp.h #include iostream int main() { try { // 以“可读写 不存在则创建”的方式打开数据库 SQLite::Database db(demo.db3, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); db.exec(CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)); std::cout 数据库已就绪 std::endl; } catch (const std::exception e) { std::cerr 出错啦 e.what() std::endl; } return 0; }看到没有打开、建表、捕获错误三个动作一气呵成。db.exec()负责执行不带返回结果的 SQL出错时直接抛异常省去一整套返回值检查。边做边学用一个小记账本吃透核心 API光看片段没感觉我们直接做一个个人记账本小工具把 SQLiteCpp 的常用招数一路练过去。建库建表两行代码搞定SQLite::Database db(book.db3, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); db.exec(CREATE TABLE IF NOT EXISTS bills (id INTEGER PRIMARY KEY, title TEXT, amount REAL, kind TEXT));插入与直查最朴素的用法// 记一笔账 db.exec(INSERT INTO bills (title, amount, kind) VALUES (早餐, 12.5, 餐饮)); // 想知道表里有多少条记录execAndGet 直接取单个值 int total db.execAndGet(SELECT COUNT(*) FROM bills).getInt(); std::cout 共记了 total 笔 std::endl;execAndGet是个很贴心的小工具专治取一个值这种高频需求。查询的正确姿势预处理语句 参数绑定直接拼 SQL 字符串既丑又危险还记得 SQL 注入吗预处理语句才是王道SQLite::Statement query(db, SELECT * FROM bills WHERE kind ?); query.bind(1, 餐饮); // 第 1 个 ? 绑定为 餐饮 while (query.executeStep()) // 每次前进一行 { int id query.getColumn(0); // 自动转换成 int std::string title query.getColumn(1); // 自动转换成 string double amount query.getColumn(2); // 自动转换成 double std::cout id | title | amount std::endl; }注意到getColumn(0)直接赋给int、std::string、double了吗这就是 SQLiteCpp 的自动类型转换魔法读取结果跟用普通变量一样自然。参数也可以按名字绑定语义更清晰SQLite::Statement query(db, SELECT * FROM bills WHERE amount :min_amount); query.bind(:min_amount, 10.0);更新与删除别忘了返回值db.exec(UPDATE bills SET amount 13.0 WHERE id 1); // 返回受影响的行数方便你确认到底删没删掉 int removed db.exec(DELETE FROM bills WHERE id 2); std::cout 删除了 removed 行 std::endl;事务保护让批量操作不再心惊胆战记账时经常要一次改多行比如转账扣 A 的钱、加 B 的钱。如果第一句成功、第二句失败钱就凭空消失了——所以必须用事务包起来。try { SQLite::Transaction transaction(db); // 构造即开启事务 db.exec(UPDATE bills SET amount amount - 100 WHERE id 1); db.exec(UPDATE bills SET amount amount 100 WHERE id 2); transaction.commit(); // 全部成功才提交 } catch (const std::exception e) { // 只要有任何一步抛异常析构函数会自动回滚 std::cerr 事务失败已自动回滚 e.what() std::endl; }这就是 RAII 事务的妙处提交是你主动调的回滚是系统兜底的。就算代码中间抛出异常数据库也不会留下半截数据。顺带一提批量写数据时套上事务性能也能肉眼可见地提升因为磁盘写入被合并了。进阶技巧把 SQLiteCpp 用出花来1. 变参绑定一行塞进全部参数C14 及以上可以少写好几行 bind#include SQLiteCpp/VariadicBind.h SQLite::Statement query(db, INSERT INTO bills (title, amount, kind) VALUES (?, ?, ?)); SQLite::bind(query, 电影票, 45.0, 娱乐); query.exec();2. 存二进制数据BLOB存图片、文件都不在话下std::vectorchar data readFile(photo.jpg); SQLite::Statement insert(db, INSERT INTO images (data) VALUES (?)); insert.bind(1, data.data(), static_castint(data.size())); insert.exec();读回来时用getBlob()拿指针、getBytes()拿长度一对儿配合使用。3. 纯内存数据库测试利器把文件名换成:memory:数据库就只活在内存里跑单元测试再合适不过SQLite::Database db(:memory:, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);4. 从异常里挖出错误码SQLite::Exception继承自std::runtime_error除了what()还能拿到数字错误码方便精确处理catch (const SQLite::Exception e) { std::cerr e.what() 错误码 e.getErrorCode() std::endl; }5. 查一下版本心里有底std::cout 运行库版本 SQLite::getLibVersion() std::endl; std::cout 编译时版本 SQLite::VERSION std::endl;常见坑与规避方法提前帮你排雷坑一execAndGet的结果要立刻拷贝。它返回的临时对象很快就会被销毁别贪图方便把它引用住直接赋给一个变量是最稳妥的。// 正确做法马上拷贝到 string std::string name db.execAndGet(SELECT name FROM users WHERE id 1).getText();坑二默认打开方式是只读。直接SQLite::Database db(x.db3)默认是OPEN_READONLY想建库建表记得显式带上OPEN_READWRITE | OPEN_CREATE不然会收到一个打不开的异常百思不得其解。坑三一个连接别跨线程。SQLiteCpp 遵循 SQLite 的多线程模式不同线程可以用不同连接但同一个 Database 连接不能同时在多个线程里用。多线程场景下给每个线程开自己的连接即可。坑四析构函数里只断言、不抛异常。这是设计使然——析构函数抛异常是灾难。如果想让断言更友好打印信息而不是直接崩定义SQLITECPP_ENABLE_ASSERT_HANDLER宏并提供自定义断言处理函数即可。坑五列元数据需要编译期开关。想用getColumnOriginName()拿到列在原始表里的名字需要SQLITE_ENABLE_COLUMN_METADATA宏在编译 SQLite 库本身时也开启否则只能拿到别名。源码地图跟着目录找到你要的东西想深入源码学习这份地图可以帮你少走弯路include/SQLiteCpp/全部头文件Database.h连接、Statement.h预处理语句、Column.h列访问、Transaction.h事务、Exception.h异常还有Backup.h、Savepoint.h、VariadicBind.h、ExecuteMany.h等进阶组件src/各类的实现源码注释相当详尽examples/example1/main.cpp一个文件串起 7 个场景从读头部信息到 BLOB 存取应有尽有强烈建议跑一遍tests/与头文件一一对应的单元测试想知道某个 API 的边界行为翻测试最快sqlite3/内置的 SQLite 源码不想装系统库时直接编这个自己动手构建并跑通测试mkdir build cd build cmake -DSQLITECPP_BUILD_EXAMPLESON -DSQLITECPP_BUILD_TESTSON .. cmake --build . ctest --output-on-failure喜欢 Meson 的话也支持meson setup builddir -DSQLITECPP_BUILD_TESTStrue -DSQLITECPP_BUILD_EXAMPLEStrue meson compile -C builddir写在最后这一趟走下来你已经掌握了 SQLiteCpp 的主干用法打开连接、建表、预处理查询、参数绑定、事务回滚还排掉了几个新手必踩的坑。它也许不是最花哨的 C SQLite 封装但那份简单、安全、像写普通 C 一样写数据库的踏实感正是它最迷人的地方。接下来怎么走建议把examples/example1/main.cpp完整跑一遍再挑tests/里你感兴趣的测试读一读——动手写两行比看十页文档都管用。把你的第一个数据库应用做出来你会发现原来在 C 里跟 SQLite 打交道也可以这么轻松愉快。【免费下载链接】SQLiteCppSQLiteC (SQLiteCpp) is a smart and easy to use C SQLite3 wrapper.项目地址: https://gitcode.com/gh_mirrors/sq/SQLiteCpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表