- 什么是SQLite
- [为什么要用 SQLite](#为什么要用 SQLite)
- [SQLite3 C/C++ API 介绍](#SQLite3 C/C++ API 介绍)
- [SQLite3 基础操作流程](#SQLite3 基础操作流程)
-
- [0. 线程安全检测](#0. 线程安全检测)
-
- [SQLite3 三种线程安全等级](#SQLite3 三种线程安全等级)
- [1. 创建 / 打开数据库文件](#1. 创建 / 打开数据库文件)
-
- [1.1 基础打开函数](#1.1 基础打开函数)
- [1.2 高阶打开函数(推荐)](#1.2 高阶打开函数(推荐))
-
- [常用 flags 参数](#常用 flags 参数)
- [2. 执行 SQL 语句](#2. 执行 SQL 语句)
- [3. 关闭数据库 & 错误处理](#3. 关闭数据库 & 错误处理)
-
- [3.1 关闭数据库句柄](#3.1 关闭数据库句柄)
- [3.2 获取错误信息](#3.2 获取错误信息)
- 简单封装
- 运行测试
什么是SQLite
SQLite 是一个 超轻量、无服务、单文件 数据库
- 不需要安装
- 不需要启动服务
- 不需要账号密码
- 整个数据库就是一个文件 test.db
为什么要用 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)中运行
SQLite3 C/C++ API 介绍
C/C++ API是SQLite3数据库的一个客户端, 提供一种用C/C++操作数据库的方法。
下面我们介绍一下常见的几个接口:
SQLite3 官方文档:https://www.sqlite.org/c3ref/funclist.html
SQLite3 基础操作流程
0. 线程安全检测
检测 SQLite3 编译阶段是否开启线程安全支持。 \
c
int sqlite3_threadsafe();
- 返回值:
0:未启用线程安全1:已启用线程安全
SQLite3 三种线程安全等级
- 非线程安全模式
- 多线程安全模式 不同线程/进程使用独立数据库连接 时安全,同一个句柄禁止多线程共用。
- 串行化模式 支持多线程/进程直接共用同一个数据库句柄。
1. 创建 / 打开数据库文件
1.1 基础打开函数
c
int sqlite3_open(const char *filename, sqlite3 **ppDb);
- 功能:打开数据库,文件不存在则自动创建
- 返回值:
SQLITE_OK代表执行成功
1.2 高阶打开函数(推荐)
可手动指定读写权限、线程安全模式。
c
int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
常用 flags 参数
SQLITE_OPEN_READWRITE:可读可写方式打开数据库SQLITE_OPEN_CREATE:数据库文件不存在则自动创建SQLITE_OPEN_NOMUTEX:多线程模式,不同线程使用独立连接即可保证安全SQLITE_OPEN_FULLMUTEX:串行化模式,多线程可共用同一个数据库句柄
2. 执行 SQL 语句
核心执行函数
c
int sqlite3_exec( sqlite3* db, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err );
结果回调函数格式
查询类 SQL 需要通过回调函数接收结果:
c
int callback(void* arg, int columnCnt, char** columnData, char** columnName);
回调参数说明
void* arg:自定义传入的参数,透传给回调函数int columnCnt:当前查询结果单行的列数char** columnData:存储当前行每一列的数据内容char** columnName:存储当前行每一列的字段名称
注意:回调函数必须返回 0 ;若返回非 0,会触发程序异常终止。 - 返回值:
SQLITE_OK代表 SQL 执行成功
3. 关闭数据库 & 错误处理
3.1 关闭数据库句柄
c
// 基础关闭 int sqlite3_close(sqlite3* db); // 推荐使用,兼容性更强 int sqlite3_close_v2(sqlite3* db);
3.2 获取错误信息
操作失败时,用于打印详细错误原因:
c
const char *sqlite3_errmsg(sqlite3* db);
简单封装
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 flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level;
int ret = sqlite3_open_v2(
_dbfile.c_str(),
&_handler,
flags,
nullptr
);
if (ret != SQLITE_OK) {
std::cerr << "【错误】打开/创建数据库失败: "
<< sqlite3_errmsg(_handler)
<< std::endl;
sqlite3_close_v2(_handler); // 失败也要释放
_handler = nullptr;
return false;
}
return true;
}
// 执行SQL语句(增删改查通用)
bool exec(
//int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err)
const std::string &sql,
SqliteCallback cb = nullptr,
void* arg = nullptr
) {
if (!_handler) {
std::cerr << "【错误】数据库未打开!" << std::endl;
return false;
}
int ret = sqlite3_exec(
_handler,
sql.c_str(),
cb,
arg,
nullptr
);
if (ret != SQLITE_OK) {
std::cerr << "【错误】SQL执行失败: " << sql << "\n"
<< sqlite3_errmsg(_handler) << std::endl;
return false;
}
return true;
}
// 关闭数据库
bool close() {
if (_handler) {
sqlite3_close_v2(_handler);
_handler = nullptr;
}
return true;
}
// 析构函数自动关闭数据库(非常重要)
~SqliteHelper() {
close();
}
private:
std::string _dbfile; // 数据库文件路径
sqlite3* _handler; // 数据库句柄
};
运行测试
cpp
#include "sqlite.hpp"
#include <cassert>
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,'王五',20);";
assert(helper.exec(insert_sql,nullptr,nullptr));
// 4.关闭数据库
helper.close();
return 0;
}
简单运行后,我们在sqlite3上查看:
bash
zhangwho@VM-0-2-ubuntu:~/mq/demo/sqlite3$ sqlite3 test.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
student
sqlite> select * from student;
1|张三|18
2|李四|19
3|王五|20
很好,继续进行删除和修改的测试:
修改:
cpp
const char *update_sql = "update student set name='赵六' where sn=1;";
assert(helper.exec(update_sql,nullptr,nullptr));
bash
sqlite> select * from student;
1|赵六|18
2|李四|19
3|王五|20
删除:
cpp
const char *delete_sql = "delete from student where sn=3;";
assert(helper.exec(delete_sql,nullptr,nullptr));
bash
sqlite> select * from student;
1|赵六|18
2|李四|19
最后进行查询测试,我们需要写一个回调函数:
cpp
int select_stu_callback(void* arg,int col_count,char** result,char** fields_name){
std::vector<std::string> *array =(std::vector<std::string>*)arg;
array->emplace_back(result[0]);//一次一行,一行一次调用
return 0;//必须返回0
}
cpp
const char *select_sql = "select name from student;";
std::vector<std::string> array;
assert(helper.exec(select_sql,select_stu_callback,&array));
for(auto &name:array){
std::cout<<name<<std::endl;
}
bash
zhangwho@VM-0-2-ubuntu:~/mq/demo/sqlite3$ ./main
赵六
李四