网络编程(八)

网络编程(八)

数据库

数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。

每个数据库都有一个或多个不同的 API 用于创建,访问,管理,搜索和复制所保存的数据。

我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢。

目的:就是为了永久保存数据

其实就是为了高效安全以及有组织的存储、访问、检索数据!

数据库的分类

关系型数据库管理系统 (RDBMS)、 非关系数据库

比较常用的是关系型数据库!

现在我们使用关系型数据库管理系统(RDBMS)来存储和管理大数据量。所谓的关系型数据库,是建立在

关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。

RDBMS 即关系数据库管理系统(Relational Database Management System)的特点:

  • 1.数据以表格的形式出现
  • 2.每行为各种记录名称
  • 3.每列为记录名称所对应的数据域
  • 4.许多的行和列组成一张表单
  • 5.若干的表单组成databas

基于嵌入式的数据库

基于嵌入式linux的数据库主要有SQLite, Firebird, Berkeley DB, eXtremeDB

Firebird是关系型数据库,功能强大,支持存储过程、SQL兼容等

Berkeley DB中并没有数据库服务器的概念,它的程序库直接链接到应用程序中

eXtremeDB是内存数据库,运行效率高

SQLite关系型数据库,体积小,支持ACID事务

注意:SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中

什么是SQLite?

SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。

为什么使用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数据库的安装

注意:安装完毕sqlite3之后,后续在.c文件中只需引入#include <sqlite3.h>即可使用它提供的API

编译代码链接第三方库:gcc *.c -o app -lsqlite3
如何进入sqlite3的环境?

---》在命令行输入: sqlite3

sqlite3中的点命令

.open 数据库文件名字

作用:打开数据库文件

存在:直接打开

不存在:新建再打开

.tables [数据库文件名]

作用:查看当前数据库文件中有哪些表

注意:.tables 数据库文件名 ---》查看指定的数据库文件中有几张表

.schema 表名

作用:查找指定的表名的字段格式(表中几个字段,字段啥类型,有无约束)

.database

作用:查看当前打开的数据库文件是谁?

.quit

作用:退出当前sqlite3操作环境

.head on

作用:显示时候,带上字段名称

.mode column

作用:显示时候,列对齐


SQLite数据库中的数据类型

SQLite数据库中的约束

sqlite3中的SQL语句

注意:

1、格式中的[]表示可有可无

2、所有语句末尾均以英文分号结尾

3、语句中的字段表示的所有字符串在书写时,应使用英文的单引号引用!

建表语句(标注出字段的个数以及类型和约束)

如何完整描述一个字段?

---》字段名 类型 [约束];

c 复制代码
create table [if not exists] 表名 (字段1 类型 [约束], 字段2 类型 [约束].....);
c 复制代码
建一张有关用户信息的表,表名为userInfo
create table if not exists userInfo (name text NOT NULL, sex text check(sex = '男' or sex = '女'));

插入语句

//给全部的字段赋值

insert into 表名 values (值1, 值2...);

注意:

字段中有个别是自增的(想着可以不写),那这时候使用上述格式,不写会报错的

处理办法:假设自增的是ID //ID name pwd

insert into userInfo values ('zhang', '123456');//error

insert into userInfo values (null, 'zhang', '123456');//ok ID

会自增

//给部分字段赋值

insert into 表名 (字段1,字段2...) values (值1, 值2...);

查找语句

select 字段1, 字段2... from 表名 where 条件;

如果需要查找表中所有信息(所有用户的所有信息):

select * from userInfo;

如果只需查看表中所有用户的密码:

slect pwd from userInfo;

如果只需查看表中用户名为hqyj的密码和ID:

select pwd, ID from userInfo where name = 'zhang';

如果查看表中年龄在25岁以上,姓名是王某的所有的且工资在6500以上(模糊查找:like子句 and运算符)

select * from userInfo where age >= 25 and name like '王_' and salary >=6500.0; //and代表且的意思 like子句是模糊查找

将表中所有用户的年龄按照从大到小排序(order by子句--》排序的)

select * from userInfo order by age;//默认是升序

select * from userInfo order by age asc;//默认是升序

select * from userInfo order by age desc;//降序
从表中检索出薪资按照降序排序之后,从低5个人开始的连续8个人的信息(分页查询技术:limit搭配offset)

select * from userInfo order by salary desc limit 8 offset 4;

修改语句

update 表名 set 字段1 = 新值1,字段2 = 新值2... where 条件;

注意:不使用where,则默认将表中的所有字段为自己要求的更改为新值,很危险!!!

update 表名 set 字段1 = 新值1,字段2 = 新值2...;//所有用户的字段全部更改为新值

删除语句

delete from 表名 where 条件;

删除表中姓名为lisi的用户信息

dellete from userInfo where name = 'lisi';

删除表

drop table 表名;

修改表中的字段个数(目前:支持增加一个班新字段)

alter table 表名 add column 新字段名 类型 约束;

给userInfo表增加一个薪资字段

alter table userInfo add column salary real check (salary >= 5000.00 and salary <= 10000.00);

sqlite3中的c/c++接口

打开数据数据sqlite3_open()

int sqlite3_open(char *path, sqlite3 **db);

功能:打开sqlite数据库

参数1:path: 数据库文件路径(数据库文件的名字可以包含路径)

db: 指向sqlite句柄的指针

返回值:成功返回0,失败返回错误码(非零值)


执行函数sqlite3_exec()

int sqlite3_exec(sqlite3 *db, const char *sql, sqlite3_callback callback, void *, char **errmsg);

功能:执行SQL操作

db:数据库句柄

sql:SQL语句

callback:回调函数

void *这里代表传递给回调函数的第一个参数。

errmsg:错误信息指针的地址

返回值:成功返回0,失败返回错误码

回调函数的定义如下:

typedef int (*sqlite3_callback)(void *para, int f_num,char **f_value, char **f_name);

功能:每找到一条记录自动执行一次回调函数。

para:传递给回调函数的参数

f_num:记录中包含的字段数目

f_value:包含每个字段值的指针数组

f_name:包含每个字段名称的指针数组

返回值:成功返回0,失败返回-1

注意:sqlite3_exec在做建表,插入,删除,修改时,回调函数是不会被执行的,因此也没有必要传参,传入NULL即可,只有在做查找时,因为需要关心查找的结果是什么,所以才会进入回调函数内部们进行查看。

登录业务:

调用sqlite3_exec()按照用户名在数据库中检索用户名对应的密码,用该密码和客户端发送过来需要登录的密码进行比对,一致回应登录成功,否则回应密码错误,如果按照用户名没有查找任何信息,则回应客户端用户不存在。

思考:调用完毕sqlite3_exec()之后,密码如何进行返回?

---》处理办法:将sqlite3_exec的第四个参数和回调函数的第一个参数搭配使用!!!

c 复制代码
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
//回调函数---》当查找到结果时,该回调函数就会被执行一次
int callback(void *arg, int f_num, char **f_value, char **f_name)
{
	printf("I am callback!\n");
	//f_num:代表本次查找到的一条记录中字段的个数(和select
	//后面传入的检索条件有关)
	//f_value:代表本次查找到的一条记录中每一个字段对应的数值(最重要!)
	//f_name:代表本次查找到的一条记录中每一个字段的名字
	printf("本次查询到的记录中字段的个数为:%d个\n",f_num);
	int i;
	for(i=0; i<f_num; i++)
	{
		printf("%s\t",f_name[i]);
	}
	printf("\n");
	printf("--------------------------------\n");
	for(i=0; i<f_num; i++)
	{
		printf("%s\t",f_value[i]);
	}
	putchar('\n');
	//完成将f_value的数值进行返回
	strcpy((char *)arg, f_value[0]);
	return 0;
}
int main(int argc, const char *argv[])
{
	//验证:数据库操作中c/c++中API 的使用
	//打开数据库
	//定义操作数据库的句柄
	sqlite3 *db;
	//int ret = sqlite3_open("MyDataBase.db", &db);
	int ret = sqlite3_open("./test/MyDataBase.db", &db);
	if(ret != SQLITE_OK)
	{
		//打印出错信息
		printf("打开数据库失败:%s\n",sqlite3_errmsg(db));
		return -1;
	}
	printf("open MyDataBase.db ok!\n");
	//建表 (userInfo表)
	//定义保存出错信息(字符串)的变量
	char *errmsg;
	char tb_name[50] = "userInfo";
	ret = sqlite3_exec(db, "create table if not exists userInfo (ID integer PRIMARY KEY AUTOINCREMENT, \
	name text NOT NULL, pwd text DEFAULT '111111');", NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("建表失败原因:%s\n",errmsg);
		//关闭句柄
		sqlite3_close(db);
		return -1;
	}
	printf("建表 %s 完毕\n",tb_name);
	//插入语句
	ret = sqlite3_exec(db, "insert into userInfo values (NULL, 'qianrushi','123456'); \
	insert into userInfo (name) values ('Java');",NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("插入数据失败:%s\n",errmsg);
		sqlite3_close(db);
		return -1;
	}
	printf("insert over!\n");
	//查找语句
	//定义地址空间,用来存储ID=2且姓名=Java用户的密码
	char retPwd[20] = {0};
	//ret = sqlite3_exec(db, "select pwd from userInfo where name = 'Java'
	and ID = 2;", \
	// &callback, retPwd, &errmsg);
	ret = sqlite3_exec(db, "select pwd from userInfo where name = 'jjjj';",
	\
	&callback, retPwd, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("select error:%s\n",errmsg);
		sqlite3_close(db);
		return -1;
	}
	if(0 == strlen(retPwd))
	{
		printf("用户不存在!\n");
		return -1;
	}
	printf("查找到的密码为:%s\n",retPwd);
	//比较密码
	char srcpwd[20] = {"111111"};//发送过来的
	if(0 == strcmp(srcpwd, retPwd))
	{
		printf("登陆成功!\n");
	}
	else
	{
		printf("密码错误!\n");
	}
	return 0;
}

思考:查找时,调用sqlite3_exec函数,每查找到一条记录,回调函数就会被执行几次,如果查找多条记录中的字段值,则会比较麻烦,因此sqlite数据库也给我们提供了一个较为方便更适合用于查找的接口:sqlite3_get_table()!!!

更适合查找的接口:sqlite3_get_table()

不使用回调函数执行SQL语句
int sqlite3_get_table(sqlite3 *db, const char *sql, char **resultp,intnrow, int *ncolumn, char **errmsg);

功能:执行SQL操作

db:数据库句柄

sql:SQL语句

resultp:用来指向sql执行结果的指针,数据集(里面存储的是查找到的结果)

nrow:满足条件的记录的数目(值为0代表没有找到相关数据!)

ncolumn:每条记录包含的字段数目

errmsg:错误信息指针的地址

返回值:成功返回0,失败返回错误码

注意:nrow=0是打印的字段名称,从1开始是有效数据的位置,所以想打印出完整的数据记录数,最后要遍历到nrow的!!!。

c 复制代码
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	//验证:数据库操作中c/c++中API 的使用
	//打开数据库
	//定义操作数据库的句柄
	sqlite3 *db;
	//int ret = sqlite3_open("MyDataBase.db", &db);
	int ret = sqlite3_open("./test/MyDataBase.db", &db);
	if(ret != SQLITE_OK)
	{
		//打印出错信息
		printf("打开数据库失败:%s\n",sqlite3_errmsg(db));
		return -1;
	}
	printf("open MyDataBase.db ok!\n");
	//建表 (userInfo表)
	//定义保存出错信息(字符串)的变量
	char *errmsg;
	char tb_name[50] = "userInfo";
	ret = sqlite3_exec(db, "create table if not exists userInfo (ID integer PRIMARY KEY AUTOINCREMENT, \
	name text NOT NULL, pwd text DEFAULT '111111');", NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("建表失败原因:%s\n",errmsg);
		//关闭句柄
		sqlite3_close(db);
		return -1;
	}
	printf("建表 %s 完毕\n",tb_name);
	//插入语句
	ret = sqlite3_exec(db, "insert into userInfo values (NULL, 'qianrushi','123456'); \
	insert into userInfo (name) values ('Java');",NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("插入数据失败:%s\n",errmsg);
		sqlite3_close(db);
		return -1;
	}
	printf("insert over!\n");
	//查找语句(sqlite3_get_table())
	//定义存储查找到一条记录结果的地址空间
	char **resultp;
	//定义存储查找到的记录的个数的变量
	int nrow;
	//定义存储每一条记录的字段个数的变量
	int ncolumn;
	//ret = sqlite3_get_table(db, "select * from userInfo;", &resultp, \
	// &nrow, &ncolumn, &errmsg);
	ret = sqlite3_get_table(db, "select pwd from userInfo where ID = 100;",&resultp, &nrow, &ncolumn, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("查找信息失败:%s\n",errmsg);
		sqlite3_close(db);
		return -1;
	}
	//如何得知查找到数据了?
	if(0 == nrow)
	{
		//没有找到
		printf("用户不存在!\n");
	}
	else
	{
		//找到了
		//遍历---》从0开始,到nrow-1结束
		int i,j;
		//下面双层for循环打印后续视情况而定,可以不选择打印,直接使用数据集中的值进行判
		断即可!
		for(i=0; i<=nrow; i++)
		{
			for(j=0; j<ncolumn;j++)
			{
			printf("%s\t", resultp[i*ncolumn+j]);
			}
			printf("\n");
		}
		//比对环节
		char srcpwd[20] = {"111111"};//假设这是客户端发送的需要登录的客户端的密码
		//和在数据库中的查找到的密码进行比对
		if(0 == strcmp(resultp[1], srcpwd))
		{
			printf("登陆成功!\n");
		}
		else
		{
			printf("密码错误!\n");
		}
	}
	return 0;
}

数据库关闭

int sqlite3_close(sqlite3 *db);

功能:关闭sqlite数据库

返回值:成功返回0,失败返回错误码

错误信息的打印

const char *sqlite3_errmsg(sqlite3 *db);

返回值:返回错误信息

相关推荐
网络安全(king)6 分钟前
网络安全攻防学习平台 - 基础关
网络·学习·web安全
李白你好23 分钟前
家用无线路由器的 2.4GHz 和 5GHz
运维·网络
嵌入(师)37 分钟前
嵌入式驱动开发详解21(网络驱动开发)
网络·驱动开发
柒烨带你飞1 小时前
路由器的原理
网络·智能路由器·php
xserver21 小时前
ensp 基于EASY IP的公司出口链路配置
网络·tcp/ip·智能路由器
枫零NET1 小时前
学习思考:一日三问(学习篇)之匹配VLAN
网络·学习·交换机
手心里的白日梦2 小时前
UDP传输层通信协议详解
网络·网络协议·udp
红米饭配南瓜汤2 小时前
WebRTC服务质量(11)- Pacer机制(03) IntervalBudget
网络·网络协议·音视频·webrtc·媒体
网安墨雨3 小时前
浅谈TARA在汽车网络安全中的关键角色
网络·web安全·汽车