SQLite数据库C_C++接口(保姆级API应用 1.4W字)(全网最详细介绍,学完必掌握)
目录
sqlite3的C/C++ API应用
前言
SQLite3库安装
API函数
打开、关闭、错误处理
打开
返回值
关闭
错误调试
实际应用
执行SQL(DDL、DML)
API介绍
实际应用
回调函数查询
API介绍
实际应用
全缓冲查询
API介绍
实际应用
字节缓冲查询
API介绍
实际应用
三种查询方式总结
sqlite实现C语言自定义函数封装
sqlite3的C/C++ API应用
前言
对于主键和外键知识点的补充:
- 主键的值不能重复,一般将自增的字段设置为主键。
- 主键是用来唯一表示一条数据的值,不能重复的。比如,一条记录包括身份正号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号薯磨是主键。
- 外键主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值或使用空值。
- 外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键。
SQLite3库安装
在 C/C++ 程序中使用 SQLite 之前,我们需要确保机器上已经有 SQLite 库,这个库提供了C/C++的操作SQLite的编程接口API。
- 在linux下只需输入安装命令:sudo apt-get install libsqlite3-dev
- QT下使用SQLite3数据库
https://www.cnblogs.com/tfanalysis/p/4073756.html
- Windows下:https://www.cnblogs.com/White-strategy-group/p/6360003.html
我使用的是Windows下通过vscode远程SSH访问Linux,因此需要在windows下也安装并添加相应的库文件。
安装前,我们在vscode中添加SQLite的头文件,提示找不到头文件
因此我们需要先定位头文件包含路径
然后到sqlite官网下载源码包(sqlite3的源码)
https://www.cnblogs.com/White-strategy-group/p/6360003.html
解压缩后文件内容如图所示
将三个.h文件添加到之前的includePath中:"D:/myinclude/**"
我们先在目录下新建一个sqlite文件夹
然后将头文件添加进来
如果发现添加完之后仍然自动找不到头文件,如下所示
我们需要手动包含具体头文件路径,如:"D:\\myinclude\\sqlite3"
我们输入sqlite3发现,可以自动提示补全,则表示库文件添加成功!
API函数
打开、关闭、错误处理
打开
第一个参数为指定要打开的数据库的名字,也包括数据库的路径
第二个参数为一个二级指针,这里的作用相当于文件描述符,它是一个数据库文件指针。传入的是一个一级指针的地址作为输出,给指针的具体指向赋值(定义一个空指针传入过来,最终会给这个指针赋值)(通过操作数据库文件指针就相对于操作数据库)
返回值
SQLite3的C/C++接口函数所有返回值如下:
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* The database file is locked */
#define SQLITE_LOCKED 6 /* A table in the database is locked */
#define SQLITE_NOMEM 7 /* A malloc() failed */
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
#define SQLITE_MISMATCH 20 /* Data type mismatch */
#define SQLITE_MISUSE 21 /* Library used incorrectly */
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* Authorization denied */
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
关闭
错误调试
返回错误信息
返回值错误码
实际应用
#include <stdio.h>
#include <sqlite3.h>int main(int argc, char const *argv[])
{sqlite3 *db;int ret = sqlite3_open(argv[1],&db);if(ret != SQLITE_OK){printf("sqlite3 open:%s\n",sqlite3_errmsg(db));exit(-1);}printf("sqlite open db successfully!\n");sqlite3_close(db);return 0;
}
编译的时候,不能直接编译
我们需要像使用POSIX库一样,手动链接sqlite3库
运行结果:
但是该函数有个bug,即使不传任何参数,也不会报错
因此最好添加一个命令行传参判断
#include <stdio.h>
#include <sqlite3.h>int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;int ret = sqlite3_open(argv[1],&db);if(ret != SQLITE_OK){printf("sqlite3 open:%s\n",sqlite3_errmsg(db));exit(-1);}printf("sqlite open db successfully!\n");sqlite3_close(db);return 0;
}
执行SQL(DDL、DML)
API介绍
执行SQL语句函数,该函数一共有5个参数
参数1:数据库文件句柄
参数2:要执行的SQL语句
参数3:回调函数,传入一个函数指针(这里的sqlite_callback callback中的sqlite_callback是通过函数指针重命名的,如下图所示)。注:回调函数只对SQL查询语句有效。当指定的是一个非查询操作,该参数应该置为NULL,否则即使传入了回调函数,回调函数也不会被执行。
参数4:回调函数的参数
参数5:保存执行SQL后的错误信息,传入的是一级指针的地址
实际应用
我们首先创建一个学生表,其中我们对错误检查进行了二次封装
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");strcpy(sql,"create table student(id integer primary key, name text, age integer)");ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec create table",db);printf("create table successfully\n");sqlite3_close(db);return 0;
}
然后通过键盘输入的方式往表里插入三条数据
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int id;char name[20];int age;int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec create table",db);printf("create table successfully\n");//插入3行数据:id,name,age 键盘输入for(int i=0;i<3;i++){printf("Please input id:\n");scanf("%d",&id);printf("Please input name:\n");scanf("%s",name);printf("Please input age:\n");scanf("%d",&age);//sql:insert into student(id,name,age)values();//sprint();写入到字符串 fprintf();写入到文件memset(sql,0,sizeof(sql));sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec create table",db);}sqlite3_close(db);return 0;
}
如果我们想要删除zhangsan的数据,可以使用如下
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#define DELETE_DATA 1
#define INSERT_DATA 0
void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int id;char name[20];int age;int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec create table",db);printf("create table successfully\n");#if INSERT_DATA//插入3行数据:id,name,age 键盘输入for(int i=0;i<3;i++){printf("Please input id:\n");scanf("%d",&id);printf("Please input name:\n");scanf("%s",name);printf("Please input age:\n");scanf("%d",&age);//sql:insert into student(id,name,age)values();//sprint();写入到字符串 fprintf();写入到文件memset(sql,0,sizeof(sql));sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec create table",db);}
#endif#if DELETE_DATAprintf("Please input who do you want to delete:\n");memset(sql,0,sizeof(sql));memset(name,0,sizeof(name));scanf("%s",name);sprintf(sql,"delete from student where name = '%s'",name);ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);print_error(ret,"sqlite exec delete data",db);
#endifsqlite3_close(db);return 0;
}
注:SQLite库提供的是原生态的接口,我们可以对其进行二次封装,像之前错误检查那样,对sqlite3_exec要执行的SQL语句,进行封装,避免代码的冗余。
回调函数查询
API介绍
每查询到一条结果就会回调一次这个函数,通过一行行缓冲数据,每次缓冲一行。
参数1:传入的参数
参数2:保存查询到的结果每一行中列的个数
参数3:保存查询到数据中每一列的值,用一个指针数组(保存指针的数组,本质是数组)来接
参数4:保存每一列的字段名字,用一个指针数组来接
实际应用
以打印查询到的结果每行列数为例:如果回调函数不加return 0;,那么将只执行一次
将上return 0;,才可以执行全部
如果想要打印查询到的每一列结果
再加上每一列相应的字段名
注意:对于外部传入回调函数的参数是无法修改的(具体原因可能是由于内部机制)
如:我们传入一个flag变量,然后出函数打印结果
我们在回调函数内对flag进行++
但出函数之后,值仍是0
如果查询不到结果,将会什么信息也不会输出
我们可以通过定义一个全局变量标志位进行判断,是否查询到数据
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>int flag=0;void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}printf("%s:successfully!\n",err);
}int my_sqlite_callback(void *para,int columnCount,char **columnValue,char**columnName)
{printf("columnCount = %d\n", columnCount);flag=1;for(int i = 0; i < columnCount;i++){printf("%s:%s|",columnName[i],columnValue[i]);}printf("\n");return 0;
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");strcpy(sql,"select * from student where name = 'zhangsan'");sqlite3_exec(db,sql,my_sqlite_callback,NULL,&errmsg);print_error(ret,"select",db);if(flag==0){printf("The data queried is empty!\n");}sqlite3_close(db);return 0;
}
全缓冲查询
API介绍
与sqlite_exec不同,sqlite3_get_table是专门用于查询数据的,通过一次性将所有查询到的数据缓冲起来
参数1:数据库文件句柄
参数2:数据库SQL语句
参数3:三维指针,用于保存查询到的结果
参数4:查询到的结果总共的行数
参数5:查询到的结果总共的列数
参数6:保存查询出错的信息
以三维指针为例,我们可以创建一个变相的二维数组:
可以想象创建一个长方体
char ***result;
result = (char***)malloc(sizeof(char**)*4);创建四个存储空间
*result = (char**)malloc(sizeof(char*)*4);每个存储空间里再创建四个存储空间
**result = (char*)malloc(sizeof(char)*4);每个存储空间里再创建一个字符串数组
最终在逻辑上形成16个连续的存储空间(物理上不连续)
我们可以通过三次for循环来创建
但访问方式仍是一维数组的访问方式,因为通过指针创建的空间,本质还是链式的,不是真正的多维数组
实际应用
实际使用时,我们需要定义一个二维指针,将它的地址作为参数传入,查询到的数据都将保存在二维指针中
发现打印是从字段开始打印的,最后少了一行数据,这是因为保存的数据包括了字段那一行,但是返回的行数nrow只算了实际数据的行数
因此需要改正如下:
这样输出的结果就是正确的了
最后一定要记得调用释放空间函数sqlite3_free_table,因为库函数sqlite3_get_table的内部分配了堆区空间
全缓冲查询程序如下:
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}printf("%s:successfully!\n",err);
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");char **result;int nrow;int ncolumn;strcpy(sql,"select * from student");ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);print_error(ret,"select",db);for(int i = 1; i<=nrow; i++){for(int j = 0; j < ncolumn; j++){printf("%s|",result[i*ncolumn+j]);}printf("\n");}sqlite3_free_table(result);sqlite3_close(db);return 0;
}
字节缓冲查询
API介绍
sqlite3_prepare
作用:把SQL语句转成字节码,由后面的执行函数去执行,将查询到的数据做字节缓冲
参数1:数据库文件句柄
参数2:SQL语句
参数3:SQL语句的最大字节数,一般设为-1
参数4:Statement句柄,即字节序句柄
参数5:SQL语句无用部分的指针,一般设为NULL
字节缓冲查询还涉及到了以下函数:
- sqlite3_step:从第一行开始查询,每次查询一行,每调用一次该函数会继续查询下一行数据,数据不为空则返回SQLITE_ROW
- sqlite3_column_count:获取结果的列数
- sqlite3_column_text:获取程序的结果当前行中每一列的数据
- sqlite3_finalize:用于销毁字节序句柄
实际应用
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>void print_error(int ret, char *err, sqlite3 *db)
{if(ret != SQLITE_OK){printf("%s:%s\n",err,sqlite3_errmsg(db));exit(-1);}printf("%s:successfully!\n",err);
}int main(int argc, char const *argv[])
{if(argc != 2){printf("Please input db name!\n");exit(-1);}sqlite3 *db;char *errmsg;char sql[1024] = {0};int ret = sqlite3_open(argv[1],&db);print_error(ret,"sqlite open",db);printf("sqlite open db successfully!\n");int rc,i,j;int ncolumn;sqlite3_stmt *stmt;strcpy(sql,"select * from student");rc = sqlite3_prepare(db,sql,-1,&stmt,NULL);if(rc){printf("query fail!\n");}else{printf("query success!\n");rc = sqlite3_step(stmt);//查询成功,则返回值rc==SQLITE_ROWncolumn = sqlite3_column_count(stmt);//获取列数while(rc == SQLITE_ROW){for(i = 0;i<ncolumn;i++){printf("%s|",sqlite3_column_text(stmt,i));//获取每一列的数据}printf("\n");rc = sqlite3_step(stmt);//继续获取下一行数据}}sqlite3_finalize(stmt);sqlite3_close(db);return 0;
}
三种查询方式总结
回调函数查询内存开销小,但查询效率相对较低;全缓冲查询的查询效率高,但是内存消耗大;字节缓冲查询兼具查询效率和低开销。(优先使用第三种查询方法)
sqlite实现C语言自定义函数封装
由于数据库提供的API接口过于复杂,使用的过程顺序也很繁琐,所以对于原生态的API在实际工作开发中,会进行一层封装,减少调用传参,减少调用次数,增加代码可读性,提高开发效率。
可封装如下:包括创建数据库、建表、插入数据、查询数据、删除数据
database.h
#ifndef _DATABASE_H_
#define _DATABASE_H_#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <libgen.h>
#include <sqlite3.h>extern int open_database(sqlite3 **db,char *database_name);
extern int create_table(sqlite3 **db,char *table_name,char *table_attribute);
extern int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg);
extern int query_data(sqlite3 **db,char ***azResult,char *table_name);
extern int delete_data(sqlite3 **db,char *table_name);#endif
database.c
#include "database.h"char sql[128];
char *zErrMsg=NULL;
int nrow=0;
int ncolumn = 0;int open_database(sqlite3 **db,char *database_name)
{int len;len = sqlite3_open(database_name,db);if(len){printf("Open database name %s failure.\n",database_name);sqlite3_close(*db);return -1;}printf("Open a sqlite3 database name %s successfully!\n",database_name);return 0;
}int create_table(sqlite3 **db,char *table_name,char *table_attribute)
{snprintf(sql,sizeof(sql),"CREATE TABLE %s(%s);",table_name,table_attribute);//log_info("sql=%s\n",sql);//sql="CREATE TABLE test(TEST CHAR(100));";if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK){printf("Table %s already exist\n",table_name);}else{printf("Create table %s successfully\n",table_name);}}int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg)
{snprintf(sql,sizeof(sql),"INSERT INTO %s(%s) VALUES('%s');",table_name,attr,msg); //插入数据if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK){sqlite3_close(*db);printf("Insert %s to table %s failure:%s\n",msg,table_name,strerror(errno));return -1;}printf("Insert %s to table %s successfully\n",msg,table_name);return 0;
}int query_data(sqlite3 **db,char ***azResult,char *table_name)
{snprintf(sql,sizeof(sql),"select *from %s;",table_name);//sql="select *from test";if(sqlite3_get_table(*db,sql,azResult,&nrow,&ncolumn,&zErrMsg)!=SQLITE_OK){sqlite3_close(*db);printf("Select *from %s failure\n",table_name);return -1;}printf("There are %d pieces of data in table %s\n",nrow,table_name);return nrow;
}int delete_data(sqlite3 **db,char *table_name)
{snprintf(sql,sizeof(sql),"delete from %s;",table_name);//sql="delete from test";if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK){sqlite3_close(*db);printf("Delete from %s failure\n",table_name);return -1;}printf("Delete data from table %s successfully!\n",table_name);return 0;
}
test_database.c
#include "database.h"int main(void)
{sqlite3 *db;char **azResult=NULL;if( open_database(&db,"test.db")<0 )return -1;if( create_table(&db,"test","TEST CHAR(100)")<0 )return -2;if( insert_data(&db,"test","TEST","Test nihao")<0 )return -3;if( query_data(&db,&azResult,"test")<0 )return -4;if( delete_data(&db,"test")<0 )return -5;if( query_data(&db,&azResult,"test")<0 )return -6;return 0;
}
相关文章:

SQLite数据库C_C++接口(保姆级API应用 1.4W字)(全网最详细介绍,学完必掌握)
目录 sqlite3的C/C API应用 前言 SQLite3库安装 API函数 打开、关闭、错误处理 打开 返回值 关闭 错误调试 实际应用 执行SQL(DDL、DML) API介绍 实际应用 回调函数查询 API介绍 实际应用 全缓冲查询 API介绍 实际应用 字节缓冲查询…...
倒计时:心理的镇静剂还是焦虑的火种?
倒计时:心理的镇静剂还是焦虑的火种? 目录 引言倒计时的作用与原理倒计时的双面性:缓解焦虑还是引发焦虑?如何正确使用倒计时结论 引言 在我们的日常生活和工作中,倒计时被广泛的应用。无论是在网购的抢购活动中&a…...
迅睿系统二开自定义函数和插件的自定义函数
全局的自定义函数: 全局的自定义函数文件:dayrui/My/Helper.php 此文件用于放网站自定义函数,程序会自动加载 当前站点的自定义函数文件:网站主目录/config/custom.php 插件的自定义函数: 基于App目录下的插件或模块…...

传统品牌如何通过3D虚拟数字人定制和动捕设备加速年轻化发展?
步入Z时代,年轻一代消费者的生活方式深受互联网技术和媒介环境影响,对新潮事物感兴趣,消费思维也相对前卫,品牌需要探索契合Z世代的消费观念,寻找新的链接拉近品牌与消费者的距离,而3D虚拟数字人定制可以帮…...

sql:SQL优化知识点记录(五)
(1)explain之例子 (2)索引单表优化案例 上面的功能已经实现,但是分析功能, 使用explain分析这条sql: 发现type为All Extra:有Using filesort (文件内排序) 这…...

1.3 Metasploit 生成SSL加密载荷
在本节中,我们将介绍如何通过使用Metasploit生成加密载荷,以隐藏网络特征。前一章节我们已经通过Metasploit生成了一段明文的ShellCode,但明文的网络传输存在安全隐患,因此本节将介绍如何通过生成SSL证书来加密ShellCodeÿ…...

redis windows 版本安装
1. 下载windows安装包并解压 如果是Linux版本可以直接到官网下载,自3.x起官网和微软网站就没有redis安装包更新了,好在github有开发者在编译发布更新(目前最新有5.0.9版本可下),地址:redis windows 5版本下…...

限流算法深入
限流定义及目的 当系统流量达到系统或下游承受能力的阈值时对系统进行限流控制以防止系统或下游挂掉,减少影响面。 限流组成:阈值及限流策略。阈值是指系统单位时间接收到的请求qps总数;限流策略是指限流行业触发后对应的系统行为ÿ…...
java 基础知识 循环的几个题目
1、输出1~100的累加和 结果显示在屏幕,显示在文件res1.txt中 2、输出1-~100的偶数和 结果显示在屏幕,显示在文件res2.txt中 3、输出所有水仙花数: 100~999的数中出现个位数的立方十位数的立方百位数的立方这个数本身 4、输出由9行9列星号组成…...
Spring Boot使用LocalDateTime、LocalDate作为入参
0x0 背景 项目中使用LocalDateTime系列作为dto中时间的类型,但是spring收到参数后总报错,为了全局配置时间类型转换,尝试了如下3中方法。 注:本文基于Springboot2.0测试,如果无法生效可能是spring版本较低导致的。PS&…...

第七周第七天学习总结 | MySQL入门及练习学习第二天
实操练习: 1.创建一个名为 cesh的数据库 2.在这个数据库内 创建一个名为 xinxi 的表要求该表可以包含:编号,姓名,备注的信息 3.为 ceshi 表 添加数据 4.为xinxi 表的数据设置中文别名 5.查询 在 xinxi 表中编号 为2 的全部…...
【考研数学】线形代数第三章——向量 | 3)向量组秩的性质、向量空间、过渡矩阵
文章目录 引言三、向量组等价、向量组的极大线性无关组与秩3.2 向量组秩的性质 四、 n n n 维向量空间4.1 基本概念4.2 基本性质 写在最后 引言 紧接前文学习完向量组秩的基本概念后,继续往后学习向量的内容。 三、向量组等价、向量组的极大线性无关组与秩 3.2 向…...

【技术】SpringBoot Word 模板替换
SpringBoot Word 模板替换 什么是 Word 模板替换如何实现 Word 模板替换 什么是 Word 模板替换 模板一般是具有固定格式的内容,其中一部分需要替换。Word 模板通俗的讲是以 Word 的形式制作模板,固定格式和内容,然后将其中的一部分数据替换掉…...
java jni nv21和nv12互转
目录 libyuv性能比较 NV12 NV21 YUV420格式介绍 jni YUV420toYUV420SemiPlanar java YUV420toYUV420SemiPlanar java NV12toYUV420SemiPlanar jni NV12toYUV420SemiPlanar...
后端面试话术集锦第二篇:spring boot面试话术
🚗后端面试集锦目录 💖后端面试话术集锦第一篇:spring面试话术💖 💖后端面试话术集锦第二篇:spring boot面试话术💖 💖后端面试话术集锦第三篇:spring cloud面试话术💖 💖后端面试话术集锦第四篇:ElasticSearch面试话术💖 💖后端面试话术集锦第五篇:r…...
Doris中分区和分桶使用教程
1 分区与分桶 Doris中有两层的数据划分,第一层是分区(Partition),第二层是分桶(Bucket), Partition又能分为Range分区和List分区。 Bucket仅支持Hash方式。 1.1 Partition 只能指定…...

电脑不安装软件,怎么将手机文件传输到电脑?
很多人都知道,AirDroid有网页版(web.airdroid.com)。 想要文件传输,却不想在电脑安装软件时,AirDroid的网页版其实也可以传输文件。 然而,要将文件从手机传输文件到网页端所在的电脑时,如果按…...
vue3 publish 出现的问题
vue3项目使用 yarn build 编译出dist文件, 发布后出现错误 #问题与解决 1)登录迭代错误(Maximum call stack size exceeded) >deepclone 的问题 在 GrandhallLayout 中判断菜单和权限中; const mainMenu cloneDeep(router.getRoutes()) lodash.clonedee…...

网络防御和入侵检测
网络防御和入侵检测是维护网络安全的关键任务,可以帮助识别和阻止未经授权的访问和恶意行为。以下是一些基本的步骤和方法,用于进行网络防御和入侵检测。 网络防御: 防火墙设置: 部署防火墙来监控和控制网络流量,阻止…...

【科研论文配图绘制】task5 SciencePlots绘图包入门
【科研论文配图绘制】task5 SciencePlots绘图包入门 task5主要学习了SciencePlots拓展包的出图样式,掌握SciencePlots的安装及具体使用。 SciencePlots作为一个专门用于科研论文绘图的第三方拓展工具包,提供了主流英文科技 期刊(如 Nature、Science 和 …...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...