SQLite3的基础使用

  • 什么是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. 非线程安全模式
  2. 多线程安全模式 不同线程/进程使用独立数据库连接 时安全,同一个句柄禁止多线程共用
  3. 串行化模式 支持多线程/进程直接共用同一个数据库句柄。

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 
赵六
李四
相关推荐
XSKY星辰天合2 小时前
XSKY 与平凯星辰(TiDB)完成联合解决方案互认证,存储+数据库联合交付能力再获验证
数据库·存储
阿丰资源2 小时前
基于SpringBoot+MySQL+Maven+Vue的旅游网站的设计与实现(源码+数据库+文档一键运行)
数据库·spring boot·mysql
pele2 小时前
如何在 Go 项目中安全、高效地共享 MySQL 数据库连接
jvm·数据库·python
qq_342295822 小时前
SQL如何用SQL子查询查找最大值对应行_关联主键优化方案
jvm·数据库·python
m0_743623922 小时前
golang如何使用iota常量生成器_golang iota常量生成器使用教程
jvm·数据库·python
字节高级特工2 小时前
迈入Redis:持久化
数据库·redis·缓存
baidu_340998822 小时前
mysql如何排查连接数爆满原因_mysql show processlist分析
jvm·数据库·python
baidu_340998822 小时前
如何用HTML函数工具测试显卡性能_基准跑分详解【详解】
jvm·数据库·python
qq_283720052 小时前
Chroma 向量数据库详细介绍与实战全攻略
数据库·人工智能·向量数据库·chroma