欢迎光临
我们一直在努力

【AI大模型接入SDK】SQLite C API编程实战:学生表CRUD案例

头像

🎬 个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:

在这里插入图片描述


文章目录

  • 1 ~> SQLite C API 编程实战:学生表 CRUD 案例
    • 1.1 SQLite 基础背景
    • 1.2 整体业务案例需求
    • 1.3 C++ 源码:可编译
    • 1.4 SQLite C API 标准执行流程
    • 1.5 关键 API 详细释义
      • 1.5.1 数据库打开关闭
      • 1.5.2 prepare 预处理
      • 1.5.3 bind 参数绑定系列
      • 1.5.4 step 执行
      • 1.5.5 column 读取结果集
      • 1.5.6 错误信息获取
      • 1.5.7 sqlite3_exec 简易执行接口
    • 1.6 Linux 编译运行要点
      • 1.6.1 编译命令
      • 1.6.2 运行
    • 1.7 RAII 封装要点与坑点
    • 1.8 SQLite 补充面试拓展知识点
    • 1.9 简述使用 SQLite C API 执行带参数 SELECT 查询
  • 结尾

在这里插入图片描述


1 ~> SQLite C API 编程实战:学生表 CRUD 案例

本案例使用 C++ 封装 SQLite3 C 原生 API,完成数据库创建、建表、增删改查完整流程,梳理 API 标准使用范式。

1.1 SQLite 基础背景

  • SQLite 是C 语言实现的文件型嵌入式数据库,无独立服务进程,数据库就是磁盘上一个.db文件。
  • 核心对象
    • sqlite3*:数据库连接句柄,代表打开的数据库实例。
    • sqlite3_stmt*:预处理语句句柄,保存预编译后的 SQL,用于参数绑定、执行、读取结果集,必须调用**sqlite3_finalize**释放,否则内存泄漏。
  • 核心返回码
    • SQLITE_OK:操作成功
    • SQLITE_DONE:语句执行完成(无返回行,insert/update/delete)
    • SQLITE_ROW:查询到一行结果,可读取列数据

1.2 整体业务案例需求

  • 创建 / 打开 SQLite 数据库文件

  • 创建学生表Student

  • 字段类型约束
    stuid INTEGER 主键,自增 AUTOINCREMENT
    name TEXT 学生姓名
    gender TEXT 学生性别
    age INTEGER 学生年龄
    gap REAL 学生学分
  • CRUD 操作:插入学生、按姓名单条查询、查询全部学生、按姓名更新、按姓名删除

  • RAII 封装:C++ 类构造打开数据库,析构自动关闭数据库连接

  • 1.3 C++ 源码:可编译

    #include <sqlite3.h>
    #include <string>
    #include <iostream>
    #include <cassert>

    // 学生信息结构体
    struct StudentInfo
    {
    std::string name;
    std::string gender;
    int age;
    double gap;
    };

    // SQLite学生数据库封装类 RAII
    class StudentDB
    {
    public:
    // 构造函数:打开数据库 + 初始化数据表
    explicit StudentDB(const std::string& dbName)
    {
    int rc = sqlite3_open(dbName.c_str(), &_db);
    if (rc != SQLITE_OK)
    {
    std::cerr << "打开数据库失败: " << sqlite3_errmsg(_db) << std::endl;
    sqlite3_close(_db);
    _db = nullptr;
    return;
    }
    // 创建学生表
    if (!initDataBase())
    {
    std::cerr << "初始化数据表失败" << std::endl;
    sqlite3_close(_db);
    _db = nullptr;
    }
    }

    // 析构函数:自动关闭数据库连接
    ~StudentDB()
    {
    if (_db != nullptr)
    {
    sqlite3_close(_db);
    _db = nullptr;
    }
    }

    // 禁止拷贝,数据库句柄不可复制
    StudentDB(const StudentDB&) = delete;
    StudentDB& operator=(const StudentDB&) = delete;

    // 插入学生信息
    bool insertStudentInfo(const StudentInfo& studentInfo)
    {
    if (_db == nullptr) return false;

    const std::string insertSQL = R"(
    INSERT INTO Student(name, gender, age, gap)
    VALUES(?,?,?,?);
    )"
    ;

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(_db, insertSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
    return false;
    }

    // 参数绑定:占位符下标从1开始
    sqlite3_bind_text(stmt, 1, studentInfo.name.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, studentInfo.gender.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_int(stmt, 3, studentInfo.age);
    sqlite3_bind_double(stmt, 4, studentInfo.gap);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    std::cerr << "执行insert失败:" << sqlite3_errmsg(_db) << std::endl;
    sqlite3_finalize(stmt);
    return false;
    }

    sqlite3_finalize(stmt);
    return true;
    }

    // 根据姓名查询单个学生
    bool queryStudentInfo(const std::string& name)
    {
    if (_db == nullptr) return false;

    const std::string querySQL = R"(
    SELECT stuid, name, gender, age, gap FROM Student WHERE name=?;
    )"
    ;

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(_db, querySQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
    return false;
    }

    sqlite3_bind_text(stmt, 1, name.c_str(), 1, SQLITE_TRANSIENT);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_ROW)
    {
    std::cerr << "未查询到该学生或者执行错误:" << sqlite3_errmsg(_db) << std::endl;
    sqlite3_finalize(stmt);
    return false;
    }

    // 读取结果集列下标从0开始
    int stuid = sqlite3_column_int(stmt, 0);
    std::string qName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)));
    std::string qGender(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
    int qAge = sqlite3_column_int(stmt, 3);
    double qGap = sqlite3_column_double(stmt, 4);

    std::cout << "==== 查询单个学生 ====" << std::endl;
    std::cout << "stuid:" << stuid << std::endl;
    std::cout << "name:" << qName << std::endl;
    std::cout << "gender:" << qGender << std::endl;
    std::cout << "age:" << qAge << std::endl;
    std::cout << "gap:" << qGap << std::endl;

    sqlite3_finalize(stmt);
    return true;
    }

    // 查询全部学生
    bool queryAllStudentInfo()
    {
    if (_db == nullptr) return false;

    const std::string queryAllSQL = R"(
    SELECT stuid, name, gender, age, gap FROM Student;
    )"
    ;

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(_db, queryAllSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
    return false;
    }

    std::cout << "\\n==== 全部学生列表 ====" << std::endl;
    rc = sqlite3_step(stmt);
    while (rc == SQLITE_ROW)
    {
    int stuid = sqlite3_column_int(stmt, 0);
    std::string qName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)));
    std::string qGender(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
    int qAge = sqlite3_column_int(stmt, 3);
    double qGap = sqlite3_column_double(stmt, 4);

    std::cout << "stuid:" << stuid
    << " | name:" << qName
    << " | gender:" << qGender
    << " | age:" << qAge
    << " | gap:" << qGap
    << std::endl;

    rc = sqlite3_step(stmt);
    }

    if (rc != SQLITE_DONE)
    {
    std::cerr << "遍历结果集异常:" << sqlite3_errmsg(_db) << std::endl;
    sqlite3_finalize(stmt);
    return false;
    }

    sqlite3_finalize(stmt);
    return true;
    }

    // 更新学生:根据姓名更新其他字段
    bool updateStudentInfo(const std::string& name, const StudentInfo& newInfo)
    {
    if (_db == nullptr) return false;

    const std::string updateSQL = R"(
    UPDATE Student SET gender=?, age=?, gap=? WHERE name=?;
    )"
    ;

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(_db, updateSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
    return false;
    }

    // 绑定顺序和SQL占位符一一对应
    sqlite3_bind_text(stmt,1, newInfo.gender.c_str(),1,SQLITE_TRANSIENT);
    sqlite3_bind_int(stmt,2, newInfo.age);
    sqlite3_bind_double(stmt,3, newInfo.gap);
    sqlite3_bind_text(stmt,4, name.c_str(),1,SQLITE_TRANSIENT);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    std::cerr << "update执行失败:" << sqlite3_errmsg(_db) << std::endl;
    sqlite3_finalize(stmt);
    return false;
    }

    sqlite3_finalize(stmt);
    return true;
    }

    // 根据姓名删除学生
    bool deleteStudentInfo(const std::string& name)
    {
    if (_db == nullptr) return false;

    const std::string deleteSQL = R"(
    DELETE FROM Student WHERE name=?;
    )"
    ;

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(_db, deleteSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    std::cerr << "prepare失败:" << sqlite3_errmsg(_db) << std::endl;
    return false;
    }

    sqlite3_bind_text(stmt, 1, name.c_str(), 1, SQLITE_TRANSIENT);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    std::cerr << "delete执行失败:" << sqlite3_errmsg(_db) << std::endl;
    sqlite3_finalize(stmt);
    return false;
    }

    sqlite3_finalize(stmt);
    return true;
    }

    private:
    sqlite3* _db = nullptr;

    // 私有:初始化数据表,创建Student表
    bool initDataBase()
    {
    const std::string createTableSQL = R"(
    CREATE TABLE IF NOT EXISTS Student(
    stuid INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT,
    gender TEXT,
    age INTEGER,
    gap REAL
    );
    )"
    ;

    char* errMsg = nullptr;
    int rc = sqlite3_exec(_db, createTableSQL.c_str(), nullptr, nullptr, &errMsg);
    if (rc != SQLITE_OK)
    {
    std::cerr << "建表失败:" << errMsg << std::endl;
    sqlite3_free(errMsg);
    return false;
    }
    return true;
    }
    };

    int main()
    {
    StudentInfo info1{"张三", "男", 18, 3.5};
    StudentInfo info2{"李四", "女", 19, 3.8};
    StudentInfo info3{"王五", "男", 20, 4.0};
    StudentInfo info4{"赵六", "女", 21, 4.2};

    // RAII打开数据库,出作用域自动关闭
    StudentDB db("studentDB.db");

    db.insertStudentInfo(info1);
    db.insertStudentInfo(info2);
    db.insertStudentInfo(info3);
    db.insertStudentInfo(info4);

    db.queryAllStudentInfo();

    // 修改王五学分
    info3.gap = 4.5;
    db.updateStudentInfo("王五", info3);
    db.queryStudentInfo("王五");

    // 删除赵六
    db.deleteStudentInfo("赵六");
    db.queryAllStudentInfo();

    return 0;
    }

    1.4 SQLite C API 标准执行流程

    所有带参数 SQL 统一遵循该 6 步流程,禁止字符串拼接 SQL,防止 SQL 注入

  • 构造 SQL 语句:使用?占位符,不要拼接用户输入字符串。
  • 预处理 SQL:调用sqlite3_prepare_v2(),生成sqlite3_stmt*预处理对象。
  • 该函数将 SQL 编译成虚拟机字节码,后续可以重复执行。
  • 绑定参数:sqlite3_bind_xxx(stmt, index, value,…)
  • 占位符下标从 1 开始;
  • SQLITE_TRANSIENT:告知 SQLite 内部复制字符串副本,函数返回后外部字符串销毁也安全。
  • 执行语句:调用sqlite3_step(stmt)
  • 增删改:期待返回SQLITE_DONE;
  • 查询单条:期待返回SQLITE_ROW;
  • 查询多条:循环调用sqlite3_step()直到返回SQLITE_DONE。
  • 读取结果集(仅查询):sqlite3_column_xxx(stmt, colIndex)
  • 列索引从 0 开始;
  • sqlite3_column_text返回const unsigned char*,需要reinterpret_cast<const char*>转为 C++ 字符串。
  • 释放预处理对象:sqlite3_finalize(stmt)
  • 强制必须调用,无论成功失败,否则内存泄漏。
  • 1.5 关键 API 详细释义

    1.5.1 数据库打开关闭

    • int sqlite3_open(const char *filename, sqlite3 **ppDb)
      • filename:数据库文件名;文件名不存在会自动创建磁盘文件。
      • ppDb:输出数据库句柄指针。
      • 返回值:SQLITE_OK成功;失败依然会返回非空句柄,必须调用sqlite3_close释放。
    • int sqlite3_close(sqlite3* db):关闭数据库连接。

    1.5.2 prepare 预处理

    • int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail)
      • nByte=-1自动计算 SQL 字符串长度;
      • ppStmt输出预处理语句指针;
      • pzTail=nullptr,忽略多余 SQL 文本。

    1.5.3 bind 参数绑定系列

    sqlite3_bind_int(stmt, idx, intVal);
    sqlite3_bind_double(stmt, idx, doubleVal);
    sqlite3_bind_text(stmt, idx, text, len, destructor);

    • destructor 参数:
      • SQLITE_TRANSIENT:SQLite 内部拷贝字符串;
      • SQLITE_STATIC:字符串生命周期由调用方保证,SQLite 不拷贝。

    1.5.4 step 执行

    • int sqlite3_step(sqlite3_stmt* stmt)
      • SQLITE_DONE:语句执行完毕,无更多结果;
      • SQLITE_ROW:获取到一行查询结果,可以读取 column。

    1.5.5 column 读取结果集

    int sqlite3_column_int(sqlite3_stmt*, int iCol);
    double sqlite3_column_double(sqlite3_stmt*, int iCol);
    const unsigned char* sqlite3_column_text(sqlite3_stmt*, int iCol);

    • iCol:列下标从 0 开始,对应 SELECT 查询字段顺序。

    1.5.6 错误信息获取

    • const char* sqlite3_errmsg(sqlite3* db):获取最近一次错误文本。
    • void sqlite3_free(void* ptr):释放sqlite3_exec返回的 errMsg 内存。

    1.5.7 sqlite3_exec 简易执行接口

    • 适合无参数 DDL 语句(建表),不适合带用户输入 DML,无参数绑定,拼接字符串会产生注入漏洞。

    1.6 Linux 编译运行要点

    1.6.1 编译命令

    # 需要链接sqlite3库
    g++ testSqlite3.cpp -o testSqlite3 -lsqlite3

    • 报错fatal error: sqlite3.h: No such file or directory:需要安装开发库

    sudo apt install libsqlite3-dev

    • 报错找不到文件:必须 cd 到源码所在目录执行编译命令。

    1.6.2 运行

    ./testSqlite3

    运行成功后当前目录生成studentDB.db数据库文件,可以使用 sqlite3 命令行工具打开查看:

    sqlite3 studentDB.db

    1.7 RAII 封装要点与坑点

  • 数据库句柄sqlite3*不能拷贝,类必须禁用拷贝构造、拷贝赋值。
  • 析构函数中调用sqlite3_close,保证对象销毁自动关闭数据库。
  • 任何分支路径(正常 return、异常 return)**sqlite3_stmt**必须 finalize,否则内存泄漏。
  • 字符串绑定优先使用SQLITE_TRANSIENT,避免野指针。
  • 禁止直接拼接 SQL 字符串,必须使用?占位符绑定参数防御 SQL 注入。
  • 1.8 SQLite 补充面试拓展知识点

    • SQLite 默认线程模式:默认单线程模式,多线程使用必须配置打开线程安全编译选项。
    • 事务:默认每条 DML 语句自动开启事务,批量插入大量数据手动开启事务可以极大提升性能。
    • 数据类型:SQLite 是动态类型数据库,列声明类型只是建议,实际存储可以存放任意类型值。
    • 自增主键:AUTOINCREMENT只用于 INTEGER 主键,不写 AUTOINCREMENT 也会自动递增,但是会复用删除过的 ID;写 AUTOINCREMENT 保证 ID 单调永不复用。

    1.9 简述使用 SQLite C API 执行带参数 SELECT 查询

  • 使用 C++ std::string 构造 SQL 语句,使用?占位符代替用户输入。
  • 调用sqlite3_prepare_v2预编译 SQL,得到sqlite3_stmt*预处理对象,校验返回码SQLITE_OK。
  • 使用sqlite3_bind_xxx系列函数,按占位符序号(从 1 开始)绑定外部传入参数。
  • 循环调用sqlite3_step,返回SQLITE_ROW代表读到一行记录;返回SQLITE_DONE代表结果集读取完毕。
  • 在每一次得到SQLITE_ROW后调用sqlite3_column_xxx读取每一列数据,列下标从 0 开始。
  • 无论执行成功还是失败,必须调用sqlite3_finalize释放 stmt 预处理对象,防止内存泄漏。
  • 通过sqlite3_errmsg捕获错误信息用于日志输出。
  • 禁止拼接用户输入字符串到 SQL 语句,防止 SQL 注入攻击。
  • **注意:**增删改操作和查询流程高度相似,区别在于 step 期待返回码为SQLITE_DONE,不需要读取 column 列数据。所有 SQLite C API 程序都遵循 prepare -> bind -> step -> finalize 范式。


    结尾

    uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

    艾莉丝努力练剑

    C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主


    👀
    【关注】 跟随我一起深耕技术领域,见证每一次成长。

    ❤️
    【点赞】 让优质内容被更多人看见,让知识传递更有力量。


    【收藏】 把核心知识点存好,在需要时随时查、随时用。

    💬
    【评论】 分享你的经验或疑问,评论区一起交流避坑!

    不要忘记给博主“一键四连”哦!

    “今日练剑达成!”

    “技术之路难免有困惑,但同行的人会让前进更有方向。”

    结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

    往期回顾:

    【AI大模型接入SDK】SQLite基础概念与C的API开发

    🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

    ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 【AI大模型接入SDK】SQLite C API编程实战:学生表CRUD案例
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址