文章目录
- [1. 什么是 SQLite](#1. 什么是 SQLite)
- [2. 为什么要用 SQLite](#2. 为什么要用 SQLite)
- [3. SQLite3 C/C++ API 介绍](#3. SQLite3 C/C++ API 介绍)
- [4. SQLite3 C/C++ API 使用](#4. SQLite3 C/C++ API 使用)
1. 什么是 SQLite
SQLite 是一个进程内的轻量级数据库,它实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,我们不需要在系统中配置。像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接,SQLite 直接访问其存储文件
2. 为什么要用 SQLite
- 不需要一个单独的服务器进程或操作的系统(无服务器的)
- SQLite 不需要配置
- 一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件
- SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于 250KiB
- SQLite 是自给自足的,这意味着不需要任何外部的依赖
- SQLite 事务是完全兼容 ACID 的,允许从多个进程或线程安全访问
- SQLite 支持 SQL92(SQL2)标准的大多数查询语言的功能
- SQLite 使用 ANSI-C 编写的,并提供了简单和易于使用的 API
- SQLite 可在 UNIX(Linux, Mac OS-X, Android, iOS)和 Windows(Win32, WinCE, WinRT)中运行
3. SQLite3 C/C++ API 介绍
C/C++ API 是 SQLite3 数据库的一个客户端, 提供一种用 C/C++操作数据库的方法。
SQLite3 官方文档:https://www.sqlite.org/c3ref/funclist.html
下面我们介绍一下常见的几个接口:
cpp
sqlite3 操作流程:
0. 查看当前数据库在编译阶段是否启动了线程安全
int sqlite3_threadsafe(); 0-未启用; 1-启用
需要注意的是 sqlite3 是有三种安全等级的:
1. 非线程安全模式
2. 线程安全模式(不同的连接在不同的线程/进程间是安全的,即一个
句柄不能用于多线程间)
3. 串行化模式(可以在不同的线程/进程间使用同一个句柄)
1. 创建/打开数据库文件,并返回操作句柄
int sqlite3_open(const char *filename, sqlite3 **ppDb) 成功返回SQLITE_OK
//若在编译阶段启动了线程安全,则在程序运行阶段可以通过参数选择线程安全等级
int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
flag:
SQLITE_OPEN_READWRITE -- 以可读可写方式打开数据库文件
SQLITE_OPEN_CREATE -- 不存在数据库文件则创建
SQLITE_OPEN_NOMUTEX--多线程模式,只要不同的线程使用不同的连接即可保证线程安全
SQLITE_OPEN_FULLMUTEX--串行化模式
zVfs:
此参数允许指定一个自定义的虚拟文件系统实现,用于适应特殊的存储环境(如加密文件系统、网络存储等)。
绝大多数情况下,传入 NULL使用默认的系统 VFS 即可。
返回:SQLITE_OK 表示成功
2. 执行语句
int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**),
void* arg, char **err)
int (*callback)(void*,int,char**,char**)
void* : 是设置的在回调时传入的 arg 参数
int:一行中数据的列数
char**:存储一行数据的字符指针数组
char**:每一列的字段名称
这个回调函数有个 int 返回值,成功处理的情况下必须返回 0,返回非 0 会触发 ABORT 退出程序
返回:SQLITE_OK 表示成功
3. 销毁句柄
int sqlite3_close(sqlite3* db); 成功返回 SQLITE_OK
int sqlite3_close_v2(sqlite3*); 推荐使用--无论如何都会返回SQLITE_OK
获取错误信息
const char *sqlite3_errmsg(sqlite3* db);
4. SQLite3 C/C++ API 使用
下面我们将这几个接口封装成一个类,快速上手这几个接口
cpp
/*
封装实现一个SqliteHelper类,提供简单的sqlite数据库操作接口,完成数据的基础增删改查操作。
1. 创建/打开数据库文件
2. 针对打开的数据库执行操作
1. 表的操作
2. 数据的操作
3. 关闭数据库
*/
#include <iostream>
#include <string>
#include <vector>
#include <sqlite3.h>
class SqliteHelper
{
public:
typedef int (*SqliteCallback)(void *, int, char **, char **);
SqliteHelper(const std::string &dbfile) : _dbfile(dbfile), _handler(nullptr) {}
bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
{
// int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
if (ret != SQLITE_OK)
{
std::cout << "创建/打开sqlite数据库失败: ";
std::cout << sqlite3_errmsg(_handler) << std::endl;
return false;
}
return true;
}
bool exec(const std::string &sql, SqliteCallback cb, void *arg)
{
// int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err)
int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
if (ret != SQLITE_OK)
{
std::cout << sql << std::endl;
std::cout << "执行语句失败: ";
std::cout << sqlite3_errmsg(_handler) << std::endl;
return false;
}
return true;
}
void close()
{
// int sqlite3_close_v2(sqlite3*);
if (_handler)
sqlite3_close_v2(_handler);
}
private:
std::string _dbfile;
sqlite3 *_handler;
};
主要方法详解
- 数据库连接管理
open方法 :使用sqlite3_open_v2函数打开或创建数据库。该方法允许通过safe_level参数(如SQLITE_OPEN_FULLMUTEX)设置线程安全级别 ,这对于多线程应用至关重要。它组合了SQLITE_OPEN_READWRITE(读写)和SQLITE_OPEN_CREATE(不存在则创建)标志,是常用的打开模式。close方法 :调用sqlite3_close_v2函数清理数据库连接资源。_v2版本是推荐使用的关闭方法。
- SQL语句执行
exec方法 :这是最核心的方法,封装了sqlite3_exec函数。它可以执行任何SQL语句,包括数据定义语言(DDL)如CREATE TABLE,以及数据操作语言(DML)如INSERT,UPDATE,DELETE,SELECT等。- 该方法支持传入一个回调函数
SqliteCallback,这对于处理SELECT查询返回的结果集非常有用。你可以在回调函数中逐行处理查询结果。 - 参数
arg可用于向回调函数传递用户自定义数据。
- 该方法支持传入一个回调函数
注意:有些功能如事务控制等我们没有实现,因为我们在后续的项目中使用不到,所以这里不过多介绍
测试程序:
cpp
#include "sqlite.hpp"
#include <cassert>
int select_stu_callback(void *arg, int col_count, char **result, char **fields_name)
{
std::vector<std::string> *arry = (std::vector<std::string> *)arg;
arry->push_back(result[0]);
return 0; // 必须有!!!
}
int main()
{
SqliteHelper helper("./test.db");
// 1. 创建/打开库文件
assert(helper.open());
// 2. 创建表(不存在则创建), 学生信息: 学号,姓名,年龄
const char *ct = "create table if not exists student(sn int primary key, name varchar(32), age int);";
assert(helper.exec(ct, nullptr, nullptr));
// 3. 新增数据 , 修改, 删除, 查询
// const char *insert_sql = "insert into student values(1, '小明', 18), (2, '小黑', 19), (3, '小红', 18);";
// assert(helper.exec(insert_sql, nullptr, nullptr));
// const char *update_sql = "update student set name='张小明' where sn=1";
// assert(helper.exec(update_sql, nullptr, nullptr));
// const char *delete_sql = "delete from student where sn=3";
// assert(helper.exec(delete_sql, nullptr, nullptr));
const char *select_sql = "select name from student;";
std::vector<std::string> arry;
assert(helper.exec(select_sql, select_stu_callback, &arry));
for (auto &name : arry)
{
std::cout << name << std::endl;
}
// 4. 关闭数据库
helper.close();
return 0;
}