首页 C语言番外:在C语言里怎么连接数据库?
文章
取消

C语言番外:在C语言里怎么连接数据库?

大家好,时隔多月,《C语言番外》又迎来了一次更新。本次更新将介绍一下在 C 语言中如何连接数据库,并遍写一个程序作为示例。

在很多学校里,C语言和数据库是两门不同的课程,所以很多同学就无法把这两种技术关联起来,我上大学的时候也是这样,这就是我写这篇公众号的原因。

在计算机的世界里,各种技术都不是独立的孤岛,而是相互关联、相辅相成的。就像在一个球队里,有守门员,有前锋,有后卫。C语言和数据库之间的关系也是如此,在没有学习数据库的时候,我们把用户输入的数据保存在数组里,这有一个非常明显的缺陷就是数组是在内存中,在断电或程序退出的情况下,内存中保存的数据就丢失了。有同学可能会说保存到文件里就可以了,数据量很小的时候确实可以,但是当数据量较大或者数据结构较复杂的情况下,将数据保存到文件的做法也会变得非常复杂。

数据库技术正是为了解决数据持久化的问题而生的。数据库非常擅长保存结构化的数据。所谓结构化的数据,就是C语言中的结构体变量、面向对象编程语言中的对象;非结构化的对象则是像图片、音频这种的。所以绝大多数的程序都会连接数据库进行数据持久化,也因此催生出多样化的数据库技术来应对多样化的需求。常见的数据库比如 Oracle、MySQL、SQL Server、PostgreSQL 都可以运行在服务器上存储大量的数据,也有用于嵌入式系统、手机上的 SQLite 数据库。

操作步骤

本文将以 SQLite 数据库为例,但是连接其他数据库也是类似的步骤。数据库编程很简单,具体步骤如下:

  1. 引入库文件。
  2. 连接数据库。
  3. 执行SQL。
  4. 关闭数据库连接。

SQLite 是一个使用 C 语言实现的非常小巧、快速的数据库引擎,常被应用于嵌入式系统、手机App中,使用 SQLite 时,系统中不需要安装额外的软件,数据库中的数据会被保存到一个单独的文件中。基于这些优点,SQLite 非常适于我们在学习、练习中使用。读者可访问SQLite官网了解更多有关 SQLite 数据库的信息。

引入库文件

我们可以在 SQLite 的官网下载到 SQLite 的源文件,包含两个文件(SQLite.h/SQLite.c),可以通过两种方式将 SQLite 引入自己的工程(二选一):

  1. 将 SQLite 源文件编译安装到自己的系统中,这样 SQLite 就成为你电脑上的一个函数库了,可以像使用标准库那样使用它。
  2. 直接将 SQLite 的源文件复制到自己的项目中。

本教程附带的示例程序采用了第一种方式。然后在用到 SQLite 的地方只需要使用#include <sqlite3.h>把 SQLite 的头文件包含进来即可。这个头文件中提供了操作 SQLite 数据库的一些函数,这样,我们就可以通过调用这些函数来愉快的使用 SQLite 了。如果你使用方式二,则只需要把引入头文件的地方改为#include "sqlite3.h"即可。

在其他编程语言中也是类似的道理,比如在 Java 中,只需要把对应数据库提供的驱动 jar 包放到自己的项目中即可。

连接数据库

连接 SQLite 数据库很简单,只需要调用sqlite3_open()函数即可,这个函数接收两个参数,第一个参数是数据库文件的位置(如果指定的文件不存在,SQLite 会自动创建一个);第二个参数是一个sqlite3 *变量的地址,连接数据库成功后,这个变量就可以代表数据库连接,后面的其他操作也会用到这个变量。

sqlite3 *db;
char *dbPath = "/path/to/database/file";
sqlite3_open(dbPath, &db);;

这个步骤的主要作用就是告诉程序我们的数据库在哪里,对于 SQLite 来说,只要一个数据库文件的保存位置即可;而对于其他数据库而言,一般需要提供数据库服务器的 IP 地址、端口、用户名、密码 等信息。 由于频繁的连接数据库、关闭数据库连接的操作是非常消耗系统资源的,所以在 Demo 中,我们在程序开始运行的时候执行一次连接数据库的操作,然后在程序结束的时候做一次关闭数据库连接的操作即可;对于运行在服务器上的数据库来说,因为服务器需要同时响应很多客户端的请求,一般会使用数据库连接池来尽量避免频繁创建、销毁数据库连接。

执行SQL

连接好数据库后,就可以在这个数据库中执行 SQL 语句了。例如,我们新创建的数据库中需要先在里面把表创建好:

char *errMsg = 0;
char *sql = "create table student(name text, age int)";
int rc = sqlite3_exec(db, sql, NULL, 0, &errMsg);
if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error: %s\n", errMsg);
    sqlite3_free(errMsg);
}

通过调用sqlite3_exec()函数在 SQLite 中执行 SQL 语句。这里对这个函数的几个参数做一个简单的介绍:

  1. 第一个参数传我们在连接数据库那一步中创建的sqlite3 *指针。
  2. 第二个参数是我们要执行的 SQL 语句,这里是char *类型。
  3. 第三个参数传的是回调函数,后面会着重讲一下如何使用这个回调函数。
  4. 第四个参数可以传任意类型的指针,SQLite 在调用回调函数时会将这个指针传给回调函数的第一个参数,这样我们就可以借助这个指针来传递一些信息了。
  5. 第五个参数错误信息,如果执行 SQL 出错了,可以通过打印这个错误信息来知道错误原因。

再讲一下回调函数,SQL 大致可以分为两类:查询执行。查询指主语句为SELECT的语句,执行则是除SELECT外的其他语句。他们区别就是查询语句有返回值而执行语句没有返回值。回调函数的作用就是用来处理返回值的,例如在SELECT name, age from student这条 SQL 语句的结果有5条记录,那么回调函数会被调用5次,每次传一条记录过来。下面是 SQLite 官网上提供的一个回调函数用法:

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
    int i;
    for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

当执行上述 SQL 语句时,这个回调函数会有如下输出:

1
2
3
4
5
6
7
8
name = zhangsan
age = 12

name = lisi
age = 15

name = wangwu
age = 22

在实际的项目开发中,一般还会用到一些 ORM(Object Relational Mapping对象关系映射) 框架。比如在 Java 中大名鼎鼎的 Hibernate、MyBatis,Golang 中的 GORM 等,这些 ORM 框架对数据库操作提供了进一步的封装,我们使用这种框架时,不需要去关心 SQL 怎么去写,只需要调用 ORM 框架提供的增删改查的函数,ORM 框架会自动生成 SQL 并执行。

关闭数据库连接

关闭数据库连接的操作也很简单:sqlite3_close(db);

Demo

本文附带的 Demo 可自行访问GitHub下载。

在这个 Demo 中,我们实现了这样的功能:

  1. 循环从控制台输入学生信息(含姓名、年龄),并保存到数据库中;
  2. 循环结束后,读取数据库中保存到所有学生信息,并打印到控制台。

在 Demo 中,我创建了 3 个 C 文件,分别是main.cstudent.cstudent_dao.c。其中student.c文件中封装了 Student 结构的定义;student_dao.c文件中封装了与数据库操作相关的功能;main.c中写了 main 函数及程序主逻辑调用。

因为程序使用了数据库存储数据,所以每次程序执行时录入的学生信息都会被保存下来。

本期 C 语言番外到此结束,have fun!

本文由作者按照 CC BY 4.0 进行授权

C语言番外:标准输入输出到底是什么?

LLM提示工程:开篇