1 sqlite的c/c++ 接口
由于早期的 SQLite 只支持 5 个 C/C++接口,因而非常容易学习和使用,但是随着 SQLite 功能的增强,新的 C/C++接口不断的增加进来,到现在有超过 150 个不同的 API 接口。这往往使初学者望而却步。幸运的是,大多数 SQLite 中的 C/C++接口是专用的,因而很少被使用到。尽管有这么多的调用接口,核心的API 仍然相对简单和便于调用。
1.1 安装 安装 SQLite 开发库
sudo apt update
sudo apt install libsqlite3-dev sqlite3
1.2 添加库
1.2.1 makefile的添加库
# 链接库(SQLite)
LIBS = -lsqlite3
1.2.2 cmake的添加库
# 查找 SQLite3 库(会查找系统默认路径)
include_directories(/usr/include)
# 链接 SQLite3 库
target_link_libraries(main PRIVATE sqlite3)
1.3 包含头文件
#include <sqlite3.h>
2 常见的函数介绍
2.1 sqlite3_open()
该接口打开与一个 SQLite 数据库文件的连接并返回一个数据库连接对象。这通常是应用程序调用的第一个 SQLite API 接口而且也是调用其他 SQLite API 接口前需要调用的接口。许多 SQLite 接口需要一个向数据库连接对象的指针作为它们的第一个参数,因而这些接口也可以理解成是数据库连接对象的操作接口。
sqlite3* db;
int rc = sqlite3_open("mydb.db", &db);
if (rc) {
std::cerr << "Can't open database: " << sqlite3_errmsg(db) << std::endl;
} else {
std::cout << "Opened database successfully\n";
}
2.2 sqlite3_prepare()或者sqlite3_prepare_v2()
接口把一个 SQL 语句文本转换成一个预处理语句对象并返回一个指向该对象的指针。这个接口需要一个由先前调用 sqlite3_open()返回的数据库连接对象指针以及一个预处理的 SQL 语句文本字符串为参数。这个 API 并不实际解析 SQL 语句,仅仅是为后续的解析而对 SQL 语句进行的预处理
int sqlite3_prepare_v2(
sqlite3 *db, // 数据库连接
const char *sql, // SQL 语句字符串
int nByte, // SQL 字符串长度(-1 表示自动计算)
sqlite3_stmt **ppStmt, // 输出:准备好的语句对象
const char **pzTail // 输出:未被使用的 SQL 语句
);
db 打开的 SQLite 数据库连接指针
sql 要执行的 SQL 语句,例如 "SELECT * FROM users WHERE id = ?"
nByte SQL 长度,设为 -1 会自动计算直到 \0
ppStmt 编译后的语句指针(sqlite3_stmt*),用于后续执行
pzTail 返回未处理的 SQL(一般可以为 nullptr)
sqlite3_stmt* stmt;
const char* sql = "SELECT name FROM users WHERE id = ?;";
// 预编译 SQL
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
std::cerr << "Prepare failed: " << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
2.3 sqlite3_step()
该接口用于解析一个由先前通过 sqlite3_prepare()接口创建的预处理语句,直至返回第一列结果为止。通过再次调用 sqlite3_step()可以返回下一列的结果,继续不断地调用 sqlite3_step()直至整个语句完成为止。对于那些并不返回结果的语句(例如:INSERT,UPDATE,DELETE 语句)一次调用 sqlite3_step()就完成了语句的处理。
int sqlite3_step(sqlite3_stmt *stmt);
返回值
| 宏定义 | 含义 | 使用说明 |
| --------------- | ------------ | ----------------------------- |
| `SQLITE_ROW` | 得到了一行查询结果 | 用于 `SELECT` |
| `SQLITE_DONE` | 操作执行完毕,无更多结果 | 用于 `INSERT` / `UPDATE` / 查询结束 |
| `SQLITE_BUSY` | 数据库正被锁定 | 多线程并发可能触发 |
| `SQLITE_ERROR` | SQL 执行出错 | SQL 语法或表错误 |
| `SQLITE_MISUSE` | API 使用错误 | 比如 stmt 已经被 finalize |
- sqlite3_prepare_v2() 编译 SQL(生成 sqlite3_stmt*)
- (可选)sqlite3_bind_*() 绑定参数
- sqlite3_step() 执行语句
- 对于 SELECT:用 sqlite3_column_*() 取出数据
- sqlite3_finalize() 清理语句
// 读取
sqlite3_stmt* stmt;
const char* sql = "SELECT id, name FROM users;";
sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const char* name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
std::cout << "ID: " << id << ", Name: " << name << "\n";
}
sqlite3_finalize(stmt);
// 插入
sqlite3_stmt* stmt;
const char* sql = "SELECT id, name FROM users;";
sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const char* name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
std::cout << "ID: " << id << ", Name: " << name << "\n";
}
sqlite3_finalize(stmt);
2.4 sqlite3_column()
该接口返回一个由 sqlite3_step()解析的预处理语句结果集中当前行的某一列数据。每次执行sqlite3_step()都返回一个新的结果集中的一行。可以多次调用 sqlite3_column()接口返回那一行中所有列的数据。就像上面所说的那样,SQLite API 中并没有 sqlite3_column()这样的接口。取而代之的是一组用于从结果集中查询出各个列项各种数据类型数据的函数接口。在这组函数接口中,有些接口返回结果集的大小,有些返回结果集的列数。
| 函数名 | 返回类型 | 示例 |
| -------------------------------- | -------------------------- | --------------------------------------------------------------- |
| `sqlite3_column_int(stmt, i)` | 整型 `int` | `int age = sqlite3_column_int(stmt, 0);` |
| `sqlite3_column_int64(stmt, i)` | 64位整型 | `sqlite3_int64 id = sqlite3_column_int64(stmt, 0);` |
| `sqlite3_column_double(stmt, i)` | 浮点数 `double` | `double price = sqlite3_column_double(stmt, 1);` |
| `sqlite3_column_text(stmt, i)` | 字符串 `const unsigned char*` | `const char* name = (const char*)sqlite3_column_text(stmt, 2);` |
| `sqlite3_column_blob(stmt, i)` | 二进制数据 `void*` | 用于图片、文件等 |
| `sqlite3_column_bytes(stmt, i)` | 字节数 | 获取文本/二进制的实际长度 |
| `sqlite3_column_type(stmt, i)` | 返回列数据类型(用于判断空值) | 见下
const char* sql = "SELECT id, name FROM users;";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const unsigned char* name = sqlite3_column_text(stmt, 1);
std::cout << "ID: " << id << ", Name: " << (name ? (const char*)name : "NULL") << "\n";
}
sqlite3_finalize(stmt);
2.5 sqlite3_finalize()
该接口销毁之前调用 sqlite3_prepare()创建的预处理语句。每一个预处理语句都必须调用这个接口进行销毁以避免内存泄漏。
// 清理
sqlite3_finalize(stmt);
2.6 sqlite3_close()
该接口关闭一个由之前调用 sqlite3_open()创建的数据库连接。所有与该连接相关的预处理语句都必须在关闭连接之前销毁
sqlite3_close(db);
2.7 sqlite3_exec()
该接口是执行上述四个步骤的一个方便的应用调用封装接口,传递给 sqlite3_exec()的回调函数用于处理每一列返回的结果集。
创建表(CREATE TABLE)
插入/更新/删除(INSERT/UPDATE/DELETE)
简单查询(可配合回调函数处理)
int sqlite3_exec(
sqlite3 *db, // 数据库连接对象
const char *sql, // SQL 语句字符串
int (*callback)(void*,int,char**,char**), // 回调函数(可为 NULL)
void *arg, // 回调函数的第一个参数(可为 NULL)
char **errmsg // 错误信息(需要释放)
);
| 参数 | 说明 |
| ---------- | ----------------------------------- |
| `db` | 已打开的数据库连接(`sqlite3*`) |
| `sql` | 要执行的 SQL 语句字符串 |
| `callback` | 每获取一行数据就调用一次的回调函数(可以为 `NULL`) |
| `arg` | 传给回调函数的第一个参数(通常用于传递上下文) |
| `errmsg` | 出错时返回错误信息指针(需要 `sqlite3_free()` 释放) |
| 返回值宏 | 含义 |
| --------------- | ----------------- |
| `SQLITE_OK` | 执行成功 |
| `SQLITE_ERROR` | SQL 错误或数据库错误 |
| `SQLITE_BUSY` | 数据库被锁定 |
| `SQLITE_MISUSE` | API 使用方式错误 |
| ... | 其他可参考 SQLite 官方文档 |
int rc = sqlite3_exec(db, sql, nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "SQL error: " << errMsg << "\n";
sqlite3_free(errMsg); // 释放错误信息
}
3 创建流程
一个应用程序可以通过执行以下几个步骤执行一条 SQL 语句:
1)使用 sqlite3_prepare()创建一个预处理语句。
2)重复调用 sqlite3_step()解析执行预处理语句。
3)对于查询操作,两次调用 sqlite3_step()之间通过 sqlite3_column()接口查询返回的结果。
4)最后,使用 sqlite3_finalize()销毁预处理语句。
上述就是有效使用 SQLite 所需要知道的知识,其余的仅仅是些需要补充的细节而已。
#include <iostream>
#include <sqlite3.h>
int main() {
sqlite3* db;
sqlite3_stmt* stmt;
// 打开数据库(如果不存在则创建)
if (sqlite3_open("example.db", &db) != SQLITE_OK) {
std::cerr << "Cannot open database: " << sqlite3_errmsg(db) << "\n";
return 1;
}
// 创建表
const char* create_sql = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);";
if (sqlite3_exec(db, create_sql, nullptr, nullptr, nullptr) != SQLITE_OK) {
std::cerr << "Table creation failed: " << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
// 插入数据(使用 ? 占位符)
const char* insert_sql = "INSERT INTO users (id, name) VALUES (?, ?);";
if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, nullptr) == SQLITE_OK) {
sqlite3_bind_int(stmt, 1, 1); // 绑定 id
sqlite3_bind_text(stmt, 2, "Alice", -1, SQLITE_TRANSIENT); // 绑定 name
if (sqlite3_step(stmt) != SQLITE_DONE) {
std::cerr << "Insert failed: " << sqlite3_errmsg(db) << "\n";
} else {
std::cout << "Inserted Alice.\n";
}
sqlite3_finalize(stmt);
}
// 查询数据
const char* select_sql = "SELECT id, name FROM users;";
if (sqlite3_prepare_v2(db, select_sql, -1, &stmt, nullptr) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
int id = sqlite3_column_int(stmt, 0);
const unsigned char* name = sqlite3_column_text(stmt, 1);
std::cout << "User: ID=" << id << ", Name=" << name << "\n";
}
sqlite3_finalize(stmt);
}
sqlite3_close(db);
return 0;
}
结果
4 总结
在 C++ 中使用 SQLite 非常轻量,适合快速开发嵌入式、桌面和离线数据应用。