数据库(sqlite)基本操作

news2025/6/9 16:03:25

数据库(sqlite)

一:简介:

为什么需要单独的数据库来进行管理数据?

  1. 数据的各种查询功能
  2. 数据的备份和恢复
  3. 花大量时间在文件数据的结构设计和维护上
  4. 要考虑多线程对数据的操作会涉及到同步问题,会增加很多额外工作
sqlite

(1)概念

SQLite是一个开源的轻量级关系数据库管理系统,它的不同之处在于它是以库的形式提供的,这意味着它是一个零配置、无需安装和管理的数据库引擎(一个Sqlite数据库,就是一个单独的文件)。它不是一个独立的服务进程,而是直接集成到应用程序中。SQLite存储整个数据库(定义、表、索引和数据)为单个跨平台的文件(.db文件),在多个系统和程序语言之间提供极致的可移植性。

注意:sqlite所有操作都是在本地直接通过对数据库文件的读写来完成的

二:sqlite安装(linux下)

1、 安装sqlite3
sudo apt install sqlite3
3、退出数据库操作终端命令
ctrl +d 
或
输入 .quit 或  .exit
4、关于数据库的一些核心概念

(1)数据库

  • 安装的sqlite3本身是一个数据库软件,通过该软件可以创建和管理多个以二维表作为数据存储手段的数据库,比如学校数据库、迷宫数据库、电影院数据库

(2)表(二维表)

  • 概念:二维表(简称表)是保存某一类数据的具体载体。一般一个数据库可以有多张二维表,每张二维表可以存储一类的数据。比如学生表(保存学生类的数据,一行数据就是一个学生),成绩表、老师表。

  • 例子:迷宫数据库

    • 应该有那些表

      • buff表

      • 敌人表

      • 地图表

        地图编号row_indexcol_indexvalue
        1001
        1011
        1021
        省略了17行
        1101
        1113
        1120
      • 玩家表:

        玩家编号玩家名称xybuff表步数地图编号
        1张三1213231
        2李四153453

(3)一行数据

  • 我们一般会将一张表的一行作为完整的一个数据。

三:表的操作

1、创建一张表
语法:
CREATE TABLE 二维表名{
   列1名称 数据类型  该列的特点,2名称 数据类型  该列的特点,
   ...
   列n名称 数据类型  该列的特点
};

--  表的最后一列末尾不加逗号
例子:构建一张学生表
create table student (
   id integer primary key autoincrement,
   name text not null,
   age integer,
   email text not null unique
);

-- 创建带时间的表
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 插入带时间的数据
INSERT INTO events (name, created_at) VALUES ('Event 2', DATETIME('now'));

其中:id为列的名称,primary key表示该列为主要列,一张表最多只有一列是主要列。一般表示该列是用于区分每一行内容的。比如每个人都有身份证号码,那么该号码就可以作为人这张表的primary key列。AUTOINCREMENT表示该列自动填充,从1开始依次递增。也就是第一行内容id为1,第二行内容id为2。INTEGER表示数据类型。数据库有很多类型。但大家只需要关注INTEGER TEXT REAL 整数、字符串、浮点数三种数据类型即可。数据类型和关键字不区分大小写。这几种只有浮点数确定是8个字节,其他数据内存大小是由数据的本身大小来决定的。

每列的特点(都是可选,根据实际需求来定):

**PRIMARY KEY:**将该列作为该表的主要参考列(简称主键),一般我们会将编号列作为主键。该列的每一个数据不能重复,一般会配合AUTOINCREMENT来使用,会自动在给表插入新数据时,该列编号自动填写并为上一个数据编号+1

NOT NULL 表示该列数据不能为空。

UNIQUE表示该列数据不能出现重复的。

DEFAULT 数据: 表示该列可以有一个默认数据,比如default 0 表示默认数据为0,前提是数据类型是 数字,如果是文本则需要加一对引号

2、列出所有表
SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;

注意

(1)sqlite-master

sqlite_master是SQLite数据库中的一个内置表,每个SQLite数据库文件都包含这个表。sqlite_master表存储了数据库的元数据,包括数据库中存在的所有表格、索引、视图以及触发器的定义信息。通常不需要更改这张表。也可以用sql语句来查看这张表里面的内容。比如SELECT type, name, sql FROM sqlite_master WHERE type='table';

sqlite_master表的结构大概如下:

  • type: 对象的类型(例如,‘table’、‘index’、‘view’、‘trigger’)。
  • name: 对象的名称。
  • tbl_name: 对象相关联的表的名称(对于表本身,nametbl_name字段将相同)。
  • rootpage: 对象在数据库文件中的起始页码。
  • sql: 创建该对象的原始SQL语句。

(2)sqlite_sequence

在SQLite数据库中,sqlite_sequence是一个内部系统表,用于跟踪自动增长(AUTOINCREMENT)字段的当前最大值。当你在表定义中为某个列使用了INTEGER PRIMARY KEY AUTOINCREMENT属性时,SQLite会使用sqlite_sequence表来存储该列最近分配的最大整数值。这样,当新记录被插入到表中时,SQLite就可以为带有AUTOINCREMENT属性的列自动分配一个比之前所有记录都大的唯一标识符。

sqlite_sequence表有两个字段:

  • name:这一列存储的是表的名称,该表含有一个AUTOINCREMENT字段。
  • seq:这一列存储的是该表中最后一个被分配的AUTOINCREMENT值。
3、查看表结构
PRAGMA table_info(表名);// 只列出自己表
sqlite> .tables
s2       student
4、删除表
DROP TABLE IF EXISTS 表名;

四:使用工具连接

SqliteBrowser

sudo apt-get install libsqlite3-dev
step3:安装Sqlite3图形化管理界面DB Browser for SQLite(用户可选择性安装):

sudo apt-get install sqlitebrowser
step4:检查是否安装成功,如果成功,输入以下命令可显示SQLite3版本信息。

五:数据的操作

sqlite推荐用单引号来包围字符串,双引号在不同版本中会有冲突

1:增加数据
-- 新增数据
-- INSERT INTO student (id, name, age, gender) values (2,'tom',26,'male')
INSERT INTO student ( name, age, gender) values ( 'lili',25,'female');
-- INSERT INTO student values ( 5, 'lili',22,'female')
-- Result: table student has 4 columns but 3 values were supplied
2:修改数据
-- 修改数据:
-- UPDATE student SET name='张三', age=50, gender = '女' WHERE id = 8

-- UPDATE student SET age=18 WHERE id = 1 or id = 3 or id = 5

-- UPDATE student SET gender='男' WHERE id = 1 and id = 3

-- 可以匹配多个id
-- UPDATE student SET age=3 WHERE id in (1,3,5, 11)
3:删除数据
-- 删除数据
-- 同样的,条件是可以跟多个的,也可以用or 或 and
-- delete from student where id = 3
-- 不跟条件,是清空表里的数据
-- delete from student
4:查找数据
-- 删除数据
-- delete from student where id = 3
-- 不跟条件,是清空表里的数据
-- delete from student

-- 查询操作:
-- * 代表显示所有的列
-- select * from student
-- select id, name , age, gender from student

-- 查询指定id的数据
--select * from student where id in (1,3,5,7,9);

--  范围查询:
-- select * from student where age >= 20 and age <= 25

-- select * from student where age between 20 and 25

-- NULL != NULL
-- select * from student where gender is NULL

-- 查询结果,可以再处理,也可以使用别名
-- select id as "学号" , name as 名字, age+10 年龄 from student where gender is not NULL

-- 模糊查询:
/*
	_ 代表任意一个字符:
	% 任意个,任意字符
*/
-- select * from student where name like '%强%'

-- 查询含有特殊字符的数据,需要使用到转义符
-- select * from student where name like '%\_%' ESCAPE '\'


-- 分页查询:
-- select * from student limit 0, 3   -- 	第一页
-- select * from student limit 3, 3   -- 	第二页
-- select * from student limit 6, 3   -- 	第三页

/*
	limit x, y;
	
	当前页:currentPage  = 2
	页大小:pageSize = 3

	x = (currentPage-1)*pageSzie;
	y = pageSize;
*/

练习:

1:分别查询出男生的id,名字和女生的id,名字

2:查询年龄大于20岁的人员信息

3:查询出年龄在15-25之间的,女性的信息

4:查询出id是 13568 男性人员的信息

5:查询出,联系电话为空的人员的信息

6:查询出,所有的名字带 ‘丽’ 的人员的信息
  • 答案:
select id, name from student where gender = 'F' 
    
select * from student where age > 20
    
select * from student where age between 15 and 25 and gender = 'female';

select * from student where gender = 'male' and id in (1,3,5,6,8)

select * from student where tel is NULL;

select * from student where name like '%丽%'

  • 查询的结果排序:
-- 排序规则默认是:asc 数字是从小到大,字母是字典顺序  desc 是倒序
-- select * from student order by name asc

-- 排序的规则后面,可以跟多个字段。
select * from student order by age desc, name desc

练习:

  • 找出年龄最大的前3个学生的信息:

  • 找出最大的年龄

select * from student order by age desc limit 0, 3

select age from student order by age desc limit 0, 1

六:常见组函数:

-- 求最大值
-- select max(age) from student

-- 求最小值 
-- select min(age) from student

-- 求平均数
-- select avg(age) from student

-- 计数
-- select count(id) from student

-- 求总和
select sum(age) from student

七:C 程序中使用sqlite3

1:使用步骤

  • 安装:使用sudo apt install libsqlite3-dev让Linux安装sqlite3开发库
  • 引入:c文件引入sqlite3.h头文件
  • 打开数据库:使用sqlite3_open函数打开数据库
  • 执行:使用sqlite3_exec来执行sql语句,比如创建表、增删查改数据等
  • 关闭数据库:使用sqlite3_close来关闭数据库
  • 编译:代码写好后进行gcc编译时,需要加上-lsqlite3参数。比如gcc xxx.c -o sqldemo -lsqlite3

2:操作函数

api大全:https://sqlite.readdevdocs.com/cintro.html

(1)sqlite3_open
  • 语法

    int sqlite3_open(const char *filename, sqlite3 **ppDb);
    
    例子:
    sqlite3 *db;
    //链接数据库
    int rc = sqlite3_open("stu.db",&db);
    
  • 功能:链接数据库

  • 参数

    • filename: 数据库文件的名称。如果文件不存在,SQLite 会尝试创建它。
    • ppDb: 是指向 sqlite3 结构体指针的指针。当函数执行成功后,这个指针会被设置为指向一个打开的数据库实例
  • 返回值

    • 函数返回一个整数状态码。SQLITE_OK (0) 表示成功,其他任何返回值都表示失败
(2)sqlite3_exec
  • 功能:执行sql语句

  • 语法-

    int sqlite3_exec(
      sqlite3*,                                  /* 打开的数据库指针 */
      const char *sql,                           /* sql语句 */
      int (*callback)(void*,int,char**,char**),  /* 回调函数:返回查询结果 */
      void *pArg,                                /* 传递给回调函数的参数,可以设置为NULL */
      char **errmsg                              /* 错误信息 */
    );
    
    /* 回调函数说明:*/
    sqlite3_callback 通常指的是传递给 sqlite3_exec() 函数的第三个参数,这是一个用户定义的回调函数。当 sqlite3_exec() 执行一个 SQL 查询(如 SELECT 语句)并返回结果行时,它会调用这个回调函数来处理每一行的数据。
    回调函数的签名是固定的,以便 SQLite 能够正确地调用它。这个签名通常如下:
    typedef int (*sqlite3_callback)(
      void*,    /* 用户提供的上下文指针,对应于 sqlite3_exec() 的第四个参数 */
      int,      /* 结果集中的列数 */
      char**,   /* 指向结果行中各列数据的指针数组 */
      char**    /* 指向结果行中各列名称的指针数组 */
    )
    
  • 参数

    • 第一个参数是一个打开的数据库实例,之前由 sqlite3_open 打开。
    • 第二个:sql 是要执行的SQL语句字符串。可以包含一个或多个由分号分隔的SQL命令。
    • 第三个:callback是一个指向函数的指针,这个函数会被每一行结果调用一次。如果不需要处理结果集,则可以为NULL。
      • 该回调函数的签名应该符合int callback(void *NotUsed, int argc, char **argv, char **azColName),其中:
        • NotUsed 是sqlite3_exec中用户提供的第四个参数。
        • argc 表示结果集中的列数。
        • argv 是一个字符串数组,包含每一列的值。
        • azColName 是一个字符串数组,包含每一列的名称。
    • 第四个参数: pArg
      • 这是一个用户提供的上下文指针,它将被传递给回调函数(如果提供了回调函数的话)。
      • 您可以使用这个指针来传递任何您希望在回调函数中访问的数据或状态信息。
      • 如果不需要传递任何上下文信息给回调函数,可以将此参数设置为 NULL
    • errmsg 如果不为NULL,任何错误信息都会被写入到它指向的位置。使用 sqlite3_free() 来释放这个错误信息的内存
  • 返回值

    • 返回 SQLITE_OK (0) 表示成功,其他值表示失败。失败时,如果 errmsg 不为NULL,它将指向一个错误消息
(3)sqlite3_free
  • 功能:释放内存,一般指sql语句执行后分配给errmsg的内存。

  • 语法

    void sqlite3_free(void*);
    
    例子:sqlite3_free(err_message);
    //err_message 是执行sql失败之后的错误信息
    
  • 参数

    • 接受一个指针,该指针必须是由sqlite分配的内存块的指针,通常是在之前的 sqlite3_exec 调用中通过 errmsg 返回的错误消息.如果sql语句执行成功,一般不用执行该函数
  • 返回值:无

(4)sqlite3_close
  • 功能:关闭一个打开的数据库连接

  • 语法

    int sqlite3_close(sqlite3*);
    例子:
    sqlite3_close(db);
    
  • 参数

    • 接受一个指向 sqlite3 结构体的指针,这个指针代表一个打开的数据库实例
  • 返回值

    • 如果成功关闭数据库,返回 SQLITE_OK。如果作为参数传递给 sqlite3_close 的数据库实例有尚未完成的操作(例如,未完成的准备语句、未关闭的BLOB句柄等),则返回 SQLITE_BUSY
(4)sqlite3_changes
  • 功能:得到数据库连接上次调用sqlite3_exec以来被修改、插入或删除的行数

  • 语法

    int  sqlite3_changes(db);
    
  • 参数

    • db:接受一个指向 sqlite3 结构体的指针,这个指针代表一个打开的数据库实例
  • 返回值

    • 得到数据库连接上次调用sqlite3_exec以来被修改、插入或删除的行数,如果没有更改,则返回0
  • 代码示例:新增数据:

    #include <stdio.h>
    #include <stdbool.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sqlite3.h>
    int main(int argc, char *argv[]) {
    	//char sql[] = "insert into student (name,password, age , gender) values ('小丽丽','1234',19,'女')";
    	//char sql[] = "update student set age = 88 where id = 1 or id = 3";
    	char sql[] = "delete from student where id = 3";
    	
    	// 1: 打开数据库
    	sqlite3 *db;
    	//链接数据库
    	int rc = sqlite3_open("school.db",&db);
    	if(rc != 0){
    		perror("数据库,打开失败!");
    		exit(EXIT_FAILURE);
    	}
    	char *errmsg = 0;
    	// 2: 执行sql
    	rc = sqlite3_exec(
    				  db,                                  /* 打开的数据库指针 */
    				  sql,                           	   /* sql语句 */
    				  NULL,						  		   /* 回调函数:返回查询结果 */
    				  NULL,                                /* 传递给回调函数的参数,可以设置为NULL */
    				  &errmsg                              /* 错误信息 */
    				);
    	if(rc == SQLITE_OK ){
    		int rows = sqlite3_changes(db);
    		printf("sql执行成功!影响数据行数:%d\n", rows);
    		
    	}else {
    		// 打印错误信息:
    		fprintf( stderr, "SQL错误:%s\n", errmsg );
    		sqlite3_free(errmsg);
    	}
    	
    	// 3: 关闭数据库
    	sqlite3_close(db);
        return 0;
    }
    
  • 查询数据:

    #include <stdio.h>
    #include <stdbool.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sqlite3.h>
    
    // 用来显示数据的回调函数
    int showData(void*,int,char**,char**);
    
    int loginState = 0;
    
    
    bool login(char* name, char* password){
    	//char sql[] = "select * from student where name = '' and password = '' ";
    	
    	char sql[128];
    	sprintf( sql, "select * from student where name = '%s' and password = '%s' ", name, password );
    
    	// 1: 打开数据库
    	sqlite3 *db;
    	//链接数据库
    	int rc = sqlite3_open("school.db",&db);
    	if(rc != 0){
    		perror("数据库,打开失败!");
    		exit(EXIT_FAILURE);
    	}
    	char *errmsg = 0;
    	// 2: 执行sql
    	rc = sqlite3_exec(
    				  db,                                  /* 打开的数据库指针 */
    				  sql,                           	   /* sql语句 */
    				  showData,						  	   /* 回调函数:返回查询结果 */
    				  NULL,                                /* 传递给回调函数的参数,可以设置为NULL */
    				  &errmsg                              /* 错误信息 */
    				);
    	if(rc == SQLITE_OK ){
    		printf("sql执行成功!\n");
    		
    	}else {
    		// 打印错误信息:
    		fprintf( stderr, "SQL错误:%s\n", errmsg );
    		sqlite3_free(errmsg);
    	}
    	
    	// 3: 关闭数据库
    	sqlite3_close(db);
    }
    
    
    int main(int argc, char *argv[]) {
    	char name[] = "abcd";
    	char password[] = "' or '1'='1";
    
    	login(name, password);
    	
    	if(loginState){
    		puts("登录成功");
    	}else {
    		puts("用户名或密码错误!");
    	}
    	
        return 0;
    }
    
    int showData(void* no_used,int cols ,char** col_values ,char** col_names){
    	printf("总列数是:%d\n", cols);
    	puts("--------------------------------");
    	for(int i = 0; i < cols; i++){
    		//loginState = 1;
    		printf("列名:%s , 值:%s \n", col_names[i], col_values[i]);
    		//return 0;
    	}
    	puts("--------------------------------");
    	
    	return 0;
    }
    

这样使用字符串,拼接方式,得到的sql语句,有注入的风险。

我们应该避免这样的情况发生。

八:sql的进阶操作:

当我们以拼接字符中的方式创建SQL语句,其实是有SQL注入风险的。我们应该避免这种情况。

1:使用预编译,对sql进行处理:
  • 语法说明:

  • sqlite3_prepare_v2() 参数说明:

    int sqlite3_prepare_v2(
      sqlite3 *db,            /* Database handle */
      const char *zSql,       /* SQL statement, UTF-8 encoded */
      int nByte,              /* Maximum length of zSql in bytes. */
      sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
      const char **pzTail     /* OUT: Pointer to unused portion of zSql */
    );
    
    1. sqlite3 *db
      • 该参数为指向 sqlite3 类型的指针,代表了一个已经打开的 SQLite 数据库连接。在调用 sqlite3_prepare_v2() 之前,你需要使用 sqlite3_open() 或者 sqlite3_open_v2() 函数来打开数据库,从而得到这个数据库连接句柄。只有借助有效的数据库连接,才能对相应的数据库开展操作。
    2. const char *zSql
      • 此参数是一个指向以 null 结尾的字符串的指针,该字符串包含了要编译的 SQL 语句。这个 SQL 语句可以是 SELECTINSERTUPDATEDELETE 等各类 SQL 命令。需要注意的是,SQL 语句里可以包含参数占位符(如 ? 或者 :name),这有助于后续绑定具体的值。
    3. int nByte
      • 该参数指定了 zSql 中 SQL 语句的最大字节数。若 nByte 为负数,那么 sqlite3_prepare_v2() 函数会把 zSql 当作以 null 结尾的字符串来处理,直至遇到字符串结束符 '\0' 为止。若 nByte 为正数,函数会处理 zSql 中的前 nByte 个字节。
    4. sqlite3_stmt **ppStmt
      • 这是一个指向 sqlite3_stmt 指针的指针。sqlite3_stmt 代表了一个准备好的语句对象,在调用 sqlite3_prepare_v2() 之后,该函数会把编译好的准备好的语句对象的地址存储到 *ppStmt 中。后续你可以使用这个准备好的语句对象来执行 SQL 语句、绑定参数等操作。在使用完毕后,要调用 sqlite3_finalize() 函数来释放这个准备好的语句对象所占用的资源。
    5. const char **pzTail
      • 这是一个指向指针的指针,用于返回 SQL 语句中未处理的部分。在 zSql 中可能包含多条 SQL 语句,当 sqlite3_prepare_v2() 编译完第一条 SQL 语句后,会把剩余部分的起始地址存储到 *pzTail 中。若 zSql 中只有一条 SQL 语句,那么 *pzTail 会指向 SQL 语句结束符 '\0' 之后的位置
  • 参数详细说明:

    const char *sql = "SELECT * FROM student where id = ? or name = ?";
    // 1:对sql进行预编译
    sqlite3_prepare_v2(db,      /* 数据库连接句柄 */
    				  sql,		/* 输入的 SQL 语句 */
    				  -1,       /* SQL 语句的最大字节数 */
    				  &stmt,    /* 指向准备好的语句对象的指针 */
    				  NULL);    /* 指向 SQL 语句中未处理部分的指针 */
    
    // 2:对sql中的?号占位符进行参数绑定
    int id = 3;
    sqlite3_bind_int(stmt, 1, id); // 绑定int值 
    
    char name[] = "tom";
    sqlite3_bind_text(stmt, 2 , name , -1, SQLITE_STATIC ); // 绑定字符串
    
    /*
    	sqlite3_bind_text 函数的参数说明:
            1: sqlite3_stmt 变量 
            2: 占位符的位置,从1开始 
            3: 占位符对应的值
            4: 字符串的长度,-1代表系统自动运算 
            5: 有三个取值:
    			5-1: SQLITE_STATIC    :在预处理的整个生命周期内都是有效的,无需复制该值,因它知在执行期间该值不会变或释放
    			5-2: SQLITE_TRANSIENT :表示提供的值在语句执行期间有效,但可能在语句执行后被释放或修改。SQLite 通常会复制这个值以确保安全
    			5-3: SQLITE_DYNAMIC	  :表示值是通过malloc()获得的且SQLite在无需使用时该释放它。通常用于希望SQLite管理内存时
    */
    // 3:注意要使用 sqlite3_step(stmt); 执行sql -----------------
    sqlite3_step(stmt);	
    
  • sqlite3_step() 函数 返回值说明;

    • 函数成功状态码:

      • SQLITE_ROW(100):表示查询结果集中存在下一行数据。当执行 SELECT 语句时,如果有结果行,sqlite3_step 会返回此值,你可以通过 sqlite3_column_ 系列函数来获取该行数据的各个列值*。
      • SQLITE_DONE(101):意味着 SQL 语句执行完毕。对于非查询语句(如 INSERT、UPDATE、DELETE),当操作完成时返回此值;对于查询语句,当遍历完所有结果行后也会返回此值。
    • 错误状态码:

      • 有很多,从1~29 代表各种不同的错误(详细看官网)
  • 详解 sqlite3_step()SQLITE_ROWSQLITE_DONE


    一、函数原型
    int sqlite3_step(sqlite3_stmt* stmt);
    

    二、返回值核心含义
    返回值含义适用语句类型
    SQLITE_ROW当前有数据行可用(仅SELECT查询)SELECT 及其变种
    SQLITE_DONE语句执行完成(INSERT/UPDATE/DELETE成功,或SELECT结果集遍历结束)所有SQL语句

    三、SQLITE_ROW 详解

    1. 触发条件

    • SELECT查询结果集中还有未读取的行

    2. 典型用法

    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, "SELECT id, name FROM users", -1, &stmt, NULL);
    
    // 循环读取每一行
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        int id = sqlite3_column_int(stmt, 0);          // 读取第0列(id)
        const char* name = sqlite3_column_text(stmt, 1); // 读取第1列(name)
        printf("ID: %d, Name: %s\n", id, name);
    }
    

    3. 关键特性

    • 必须用while循环处理多行结果
    • 每次返回SQLITE_ROW时,需要用sqlite3_column_*函数读取当前行数据

    四、SQLITE_DONE 详解

    1. 触发场景

    语句类型含义
    SELECT结果集已全部遍历完成(没有更多数据行)
    INSERT/UPDATE/DELETESQL语句执行成功,数据已修改
    其他语句语句执行完成(如CREATE TABLE)

    2. 典型用法
    场景1:判断写操作成功

    // INSERT示例
    sqlite3_prepare_v2(db, "INSERT INTO logs(msg) VALUES('error')", -1, &stmt, NULL);
    if (sqlite3_step(stmt) == SQLITE_DONE) {
        printf("插入成功,影响行数:%d\n", sqlite3_changes(db));
    } else {
        printf("失败:%s\n", sqlite3_errmsg(db));
    }
    

    场景2:SELECT结束检测

    while (sqlite3_step(stmt) == SQLITE_ROW) { /* 读取数据 */ }
    // 循环退出时自动达到SQLITE_DONE状态
    printf("查询完成\n");
    

    五、对比案例
    案例1:SELECT查询
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, "SELECT * FROM products", -1, &stmt, NULL);
    
    int row_count = 0;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        printf("产品:%s\n", sqlite3_column_text(stmt, 1));
        row_count++;
    }
    printf("共查询到%d条数据\n", row_count);  // 循环退出时即SQLITE_DONE状态
    
    案例2:INSERT操作
    sqlite3_prepare_v2(db, "INSERT INTO orders VALUES(1001, 'book')", -1, &stmt, NULL);
    if (sqlite3_step(stmt) == SQLITE_DONE) {
        printf("订单创建成功!\n");
    } else {
        printf("错误码:%d\n", sqlite3_errcode(db));
    }
    

    六、特殊注意事项
    1. 必须检查返回值
      错误示例:

      sqlite3_step(stmt);  // ❌ 不检查返回值可能导致未知错误
      
    2. 重置语句
      如需复用语句对象,必须调用sqlite3_reset()

      sqlite3_reset(stmt);  // 重置到可执行状态
      sqlite3_clear_bindings(stmt);  // 可选:清除绑定参数
      
    3. 多语句执行
      批量执行SQL时,每次sqlite3_step()只推进一个语句:

      // 执行多条SQL
      sqlite3_prepare_v2(db, "INSERT INTO t1 VALUES(1); INSERT INTO t2 VALUES(2);", ...);
      while (sqlite3_step(stmt) == SQLITE_ROW) {}  // 忽略SELECT结果
      if (sqlite3_step(stmt) == SQLITE_DONE) {
          printf("所有语句执行完成\n");
      }
      

    七、流程图解
    SELECT
    INSERT/UPDATE/DELETE
    开始执行sqlite3_step
    语句类型
    返回SQLITE_ROW?
    读取当前行数据
    返回SQLITE_DONE
    执行修改
    成功?
    返回SQLITE_DONE
    返回错误码

    八、总结
    • SQLITE_ROW

      • SELECT查询的"数据就绪"信号
      • 必须配套使用sqlite3_column_*读取数据
      • 需要循环处理直到返回SQLITE_DONE
    • SQLITE_DONE

      • 所有语句的"执行完成"信号
      • 写操作的成功标志
      • SELECT查询的结束标志
  • 详解 sqlite3_changes()sqlite3_errmsg()


    1. sqlite3_changes():获取受影响的行数

    作用
    返回最近一次 INSERT/UPDATE/DELETE 操作影响的数据行数。

    函数原型

    int sqlite3_changes(sqlite3* db);
    

    关键特性

    • 立即生效:只反映最近一次操作的修改量
    • 作用范围:针对当前数据库连接(sqlite3* db
    • 返回值
      • 正整数:实际受影响的行数
      • 0:语句执行但未修改数据(如 UPDATE 条件不匹配)

    典型用法

    // 执行UPDATE操作
    sqlite3_exec(db, "UPDATE users SET status=1 WHERE age>18", NULL, NULL, NULL);
    
    // 获取影响行数
    int rows_updated = sqlite3_changes(db);
    printf("更新了%d条用户数据\n", rows_updated);
    

    注意事项

    • 不适用于 SELECT 语句(返回0)
    • 批量操作时只记录最后一条语句的影响

    2. sqlite3_errmsg():获取错误信息

    作用
    返回最近一次SQLite操作的错误描述(人类可读的字符串)。

    函数原型

    const char* sqlite3_errmsg(sqlite3* db);
    

    关键特性

    • 错误覆盖:包括预处理、执行、约束违反等所有阶段错误
    • 返回值
      • 错误描述字符串(如 "UNIQUE constraint failed"
      • 成功时为 "not an error"

    典型用法

    if (sqlite3_step(stmt) != SQLITE_DONE) {
        printf("操作失败:%s\n", sqlite3_errmsg(db));
        // 输出示例:"operation failed: UNIQUE constraint failed: users.email"
    }
    

    搭配函数

    int errcode = sqlite3_errcode(db);  // 获取错误码(如SQLITE_CONSTRAINT)
    

    3. 对比总结
    函数作用适用场景返回值类型
    sqlite3_changes()获取写操作影响行数INSERT/UPDATE/DELETE整数
    sqlite3_errmsg()获取错误描述任何操作失败时字符串
2:预编译使用:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>

// 用来显示数据的回调函数
int showData(void*,int,char**,char**);

bool login(char* name, char* password){
	
	char sql[] = "select * from student where name = ? and password = ? ";
	
	// 1: 打开数据库
	sqlite3 *db;
	//链接数据库
	int rc = sqlite3_open("school.db",&db);
	if(rc != 0){
		perror("数据库,打开失败!");
		exit(EXIT_FAILURE);
	}
	char *errmsg = 0;
	// 2: -------------------------------------------------
	// 2-1:执行sql的预编译:
	sqlite3_stmt* stmt;
	
	sqlite3_prepare_v2(
					  db,            	/* Database handle */
					  sql,       		/* SQL statement, UTF-8 encoded */
					  -1,              	/* Maximum length of zSql in bytes. */
					  &stmt,  			/* OUT: Statement handle */
					  NULL     			/* OUT: Pointer to unused portion of zSql */
					);
	// 2-2: 对问号进行赋值:
	// int sqlite3_bind_int(sqlite3_stmt*, int, int);  // 绑定int值,使用的函数
	// 把name绑定到1号位置的问号
	sqlite3_bind_text( stmt ,1 , name , -1 , SQLITE_STATIC );
	sqlite3_bind_text( stmt ,2 , password , -1 , SQLITE_STATIC );
	
	// 2-3: 执行:
	bool f = false;
	if(sqlite3_step(stmt)== SQLITE_ROW){
		// 2-4: 得到查询的结果
		int id = sqlite3_column_int(stmt, 0);
		char name[32] = {0};
		strcpy(name, sqlite3_column_text(stmt, 1));
		char password[32] = {0};
		strcpy(password, sqlite3_column_text(stmt, 2));
		
		printf("id是:%d, 用户名是:%s , 密码:%s \n", id, name, password);
		f = true;
	}
	// 3: 关闭数据库
	sqlite3_close(db);
	return f;
}

void showAll(){
	char sql[] = "select * from student ";
	
	// 1: 打开数据库
	sqlite3 *db;
	//链接数据库
	int rc = sqlite3_open("school.db",&db);
	if(rc != 0){
		perror("数据库,打开失败!");
		exit(EXIT_FAILURE);
	}
	char *errmsg = 0;
	// 2: -------------------------------------------------
	// 2-1:执行sql的预编译:
	sqlite3_stmt* stmt;
	
	sqlite3_prepare_v2(
					  db,            	/* Database handle */
					  sql,       		/* SQL statement, UTF-8 encoded */
					  -1,              	/* Maximum length of zSql in bytes. */
					  &stmt,  			/* OUT: Statement handle */
					  NULL     			/* OUT: Pointer to unused portion of zSql */
					);
	// 2-3: 执行:
	while(sqlite3_step(stmt)== SQLITE_ROW){
		// 2-4: 得到查询的结果
		int id = sqlite3_column_int(stmt, 0);
		char name[32] = {0};
		strcpy(name, sqlite3_column_text(stmt, 1));
		char password[32] = {0};
		strcpy(password, sqlite3_column_text(stmt, 2));
		int age = sqlite3_column_int(stmt, 3);
		char gender[32] = {0};
		strcpy(gender, sqlite3_column_text(stmt, 4));
		
		printf("id是:%d, 用户名是:%s , 密码:%s  , 年龄:%d , 性别:%s\n", id, name, password, age, gender);
	}
	// 要释放stmt内存
	sqlite3_finalize(stmt);
	// 3: 关闭数据库
	sqlite3_close(db);

}

int main(int argc, char *argv[]) {
/*
	char name[] = "jack";
	char password[] = "loverose";

	bool f = login(name, password);
	
	if(f){
		puts("登录成功");
	}else {
		puts("用户名或密码错误!");
	}
*/

	showAll();
    return 0;
}

为什么在查询数据时使用 SQLITE_ROW 而不是 SQLITE_DONE


1. 核心区别
返回值含义适用场景
SQLITE_ROW当前有数据行可读取仅用于 SELECT 查询,表示结果集中有数据
SQLITE_DONE语句执行完成用于所有SQL语句,表示操作结束(无更多数据或操作完成)

2. 代码场景分析

您的代码是典型的 SELECT查询数据读取逻辑

if(sqlite3_step(stmt) == SQLITE_ROW) {  // ✅ 正确用法
    int id = sqlite3_column_int(stmt, 0);
    char name[32] = {0};
    strcpy(name, sqlite3_column_text(stmt, 1));
    // ...读取其他列...
}
为什么不能用 SQLITE_DONE
  1. 逻辑矛盾

    • SQLITE_DONE 表示"没有数据可读"或"操作完成"
    • 如果用它作为条件,代码块永远不会执行(因为当返回 SQLITE_DONE 时,已经无法读取数据)
  2. 执行流程

    • sqlite3_step(stmt) 首次调用时:
      • 如果有数据 → 返回 SQLITE_ROW
      • 如果无数据 → 直接返回 SQLITE_DONE
    • == SQLITE_DONE 判断会直接跳过数据读取

3. 正确模式对比
SELECT查询(必须用 SQLITE_ROW
// 正确写法:循环读取每一行
while (sqlite3_step(stmt) == SQLITE_ROW) {
    // 读取当前行数据
    int id = sqlite3_column_int(stmt, 0);
    // ...
}
// 退出循环时,说明已返回 SQLITE_DONE(数据读完)
INSERT/UPDATE/DELETE(用 SQLITE_DONE
// 正确写法:判断操作是否完成
if (sqlite3_step(insert_stmt) == SQLITE_DONE) {
    printf("插入成功,影响行数:%d\n", sqlite3_changes(db));
}

4. 底层原理
  • SQLITE_ROW 机制

    • SQLite 内部维护一个结果集游标
    • 每次 sqlite3_step() 推进游标到下一行
    • 返回 SQLITE_ROW 时,游标指向有效数据行
  • SQLITE_DONE 触发时机

    • SELECT:游标越过最后一行
    • INSERT/UPDATE/DELETE:语句执行完毕

5. 错误用法示例
// ❌ 错误写法:永远读不到数据
if (sqlite3_step(stmt) == SQLITE_DONE) {
    // 这里不会执行,因为:
    // 1. 如果有数据,先返回 SQLITE_ROW
    // 2. 如果无数据,条件成立但已无数据可读
    int id = sqlite3_column_int(stmt, 0); // 无效操作!
}

6. 完整查询流程
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, "SELECT id, name FROM users", -1, &stmt, NULL);

// 方法1:单行查询(非循环)
if (sqlite3_step(stmt) == SQLITE_ROW) {
    printf("第一行数据:id=%d\n", sqlite3_column_int(stmt, 0));
}

// 方法2:多行查询(循环)
while (sqlite3_step(stmt) == SQLITE_ROW) {
    printf("id=%d, name=%s\n", 
           sqlite3_column_int(stmt, 0),
           sqlite3_column_text(stmt, 1));
}

sqlite3_finalize(stmt);

7. 总结
  • SQLITE_ROW

    • SELECT查询的"数据就绪"信号
    • 唯一用于读取数据的判断条件
  • SQLITE_DONE

    • 所有SQL语句的"终止"信号
    • 用于确认操作完成,不能用于读取数据

关键记忆点

要读数据,等 ROW
操作结束,看 DONE

SQLite 结果集指针机制详解

1. 指针(游标)的工作方式

当执行 SELECT 查询时,SQLite 内部会创建一个结果集游标,其行为如下:

游标状态返回值数据访问能力
指向有效行SQLITE_ROW可通过 sqlite3_column_* 读取当前行
越过最后一行SQLITE_DONE无法再读取数据
2. 数据检索流程
首次调用sqlite3_step
有数据?
返回SQLITE_ROW,游标锁定当前行
返回SQLITE_DONE
用sqlite3_column_*读取数据
再次调用sqlite3_step
3. 关键区别
场景SQLITE_ROWSQLITE_DONE
游标位置停在有效数据行已越过结果集末尾
数据可读性可立即读取当前行无法读取任何数据
典型代码位置while(sqlite3_step()==SQLITE_ROW)循环结束后判断
4. 技术细节
  1. 游标初始化

    • 执行 sqlite3_prepare_v2() 后,游标位于结果集起始位置之前
    • 首次 sqlite3_step() 将游标移动到第一行(如果有数据)
  2. 数据访问时机

    // 游标状态变化示例:
    // 初始状态:游标在结果集起始位置之前
    sqlite3_step(stmt);  // 游标移动到第1行,返回SQLITE_ROW
    sqlite3_column_int(stmt,0);  // 读取第1行数据
    sqlite3_step(stmt);  // 游标移动到第2行(或返回SQLITE_DONE)
    
  3. 边界情况

    • 空结果集:首次 sqlite3_step() 直接返回 SQLITE_DONE
    • 最后一行:最后一次返回 SQLITE_ROW 后,下次调用返回 SQLITE_DONE
5. 正确用法模板
// 准备语句
sqlite3_prepare_v2(db, "SELECT id,name FROM table", -1, &stmt, NULL);

// 遍历结果集
while(sqlite3_step(stmt) == SQLITE_ROW) {
    // 游标有效时读取数据
    int id = sqlite3_column_int(stmt, 0);
    const char* name = sqlite3_column_text(stmt, 1);
}

// 检查是否正常结束
if(sqlite3_errcode(db) == SQLITE_DONE) {
    printf("成功遍历所有数据\n");
}
**6. 常见误区解释
  • 误区:“SQLITE_DONE 也可以用来读数据”

    • 事实:当返回 SQLITE_DONE 时,游标已失效,任何 sqlite3_column_* 调用都将返回无意义数据
  • 误区:“每次调用 sqlite3_step() 都会执行一次查询”

    • 事实:查询只执行一次,sqlite3_step() 只是移动游标位置
7. 性能优化提示
  1. 在获取文本数据时,优先使用:

    const unsigned char* text = sqlite3_column_text(stmt, col);
    // 而不是拷贝操作(如strcpy)
    
  2. 对于大量数据处理:

    sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, 0);
    while(sqlite3_step(stmt) == SQLITE_ROW) {
        // 批量处理
    }
    sqlite3_exec(db, "COMMIT", 0, 0, 0);
    

关于 sqlite3_step()sqlite3_reset() 的正确使用场景

1. 核心区别
函数作用使用场景是否改变语句状态
sqlite3_step()推进语句执行/移动结果集游标每次需要读取新数据或执行操作时调用会改变内部状态
sqlite3_reset()将语句重置到初始状态需要重复使用预处理语句时调用恢复初始状态

2. 为什么示例中不需要 sqlite3_reset()

在之前的SELECT查询示例中:

while (sqlite3_step(stmt) == SQLITE_ROW) {
    // 读取数据...
}

不需要 reset 的原因是

  1. 单次查询生命周期

    • preparestep循环 → finalize
    • 语句对象即将被销毁(finalize),无需重置
  2. 游标自动管理

    • sqlite3_step() 返回 SQLITE_DONE 时,游标已自动失效
    • 如果不再使用该语句,直接 finalize 更高效

3. 必须使用 sqlite3_reset() 的场景
场景1:重复执行同一条语句
// 准备可复用的语句
sqlite3_prepare_v2(db, "INSERT INTO logs(msg) VALUES(?)", -1, &stmt, NULL);

// 第一次执行
sqlite3_bind_text(stmt, 1, "Error1", -1, SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_reset(stmt);  // ⚠️ 必须重置!否则下次绑定会失败

// 第二次执行
sqlite3_bind_text(stmt, 1, "Error2", -1, SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
场景2:批量参数化查询
sqlite3_prepare_v2(db, "UPDATE users SET score=? WHERE id=?", -1, &stmt, NULL);

for (int i = 0; i < 100; i++) {
    sqlite3_bind_int(stmt, 1, scores[i]);
    sqlite3_bind_int(stmt, 2, ids[i]);
    sqlite3_step(stmt);
    sqlite3_reset(stmt);  // ⚠️ 必须重置才能绑定新参数
}

4. 不需要 reset 的例外情况
场景处理方式
语句只用一次直接 finalize
连续执行非参数化语句直接多次 step(如执行多个独立SQL)

5. 底层原理图解
sqlite3_step()
返回SQLITE_ROW
再次调用step()
返回SQLITE_DONE
sqlite3_reset()
sqlite3_finalize()
Prepared
Executing
HasRow
Done

6. 错误用法示例
// ❌ 错误:未reset导致绑定失败
sqlite3_prepare_v2(db, "INSERT INTO t VALUES(?)", -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, 100);
sqlite3_step(stmt);

// 直接再次绑定会失败!
sqlite3_bind_int(stmt, 1, 200);  // 错误:SQLITE_MISUSE
sqlite3_step(stmt);

修复方法

// ✅ 正确:重置后复用
sqlite3_reset(stmt);  // 重置语句状态
sqlite3_clear_bindings(stmt);  // 可选:清除旧绑定
sqlite3_bind_int(stmt, 1, 200);
sqlite3_step(stmt);

7. 最佳实践总结
  1. 查询数据(SELECT):

    • 单次查询:无需 reset,直接 finalize
    • 复用查询:reset + 重新绑定参数
  2. 修改数据(INSERT/UPDATE/DELETE):

    • 批量操作:每次执行后必须 reset
    • 单次操作:直接 finalize
  3. 事务处理

    sqlite3_exec(db, "BEGIN", 0, 0, 0);
    for (...) {
        sqlite3_reset(stmt);
        // 绑定参数...
        sqlite3_step(stmt);
    }
    sqlite3_exec(db, "COMMIT", 0, 0, 0);
    

8. 性能影响
  • 不必要地调用 reset:增加约 5-10% 的性能开销
  • 漏掉必要的 reset:导致内存泄漏或绑定失败

最终结论

  • 需要 reset:当你想复用预处理语句时(特别是带参数的语句)
  • 不需要 reset:当语句即将被销毁finalize)或只执行一次

记忆口诀

一用到底就销毁(finalize),
想再复用需重置(reset)!

1: 分别封装四个函数:完成增删改查操作<要使用预编译完成>:
  • 增加:根据User结构体,增加一条数据
  • 修改:根据User结构体,修改一条数据
  • 删除:根据id删除一条数据
  • 查询:
    • 根据id查询相应的数据,得到一个User结构体数据
    • 查询所有数据,得到一个User结构体数组

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2405556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++定长内存块的实现

内存池 内存池是指程序预先从操作系统 申请一块足够大内存 &#xff0c;此后&#xff0c;当程序中需要申请内存的时候&#xff0c;不是直接向操作系统申请&#xff0c;而是 直接从内存池中获取 &#xff1b; 同理&#xff0c;当 **程序释放内存 **的时候&#xff0c;并不真正将…

Unity使用代码分析Roslyn Analyzers

一、创建项目&#xff08;注意这里不要选netstandard2.1会有报错&#xff09; 二、NuGet上安装Microsoft.CodeAnalysis.CSharp 三、实现[Partial]特性标注的类&#xff0c;结构体&#xff0c;record必须要partial关键字修饰 需要继承DiagnosticAnalyzer 注意一定要加特性Diagn…

大数据CSV导入MySQL

CSV Import MySQL 源码主要特性技术栈快速开始1. 环境要求2. 构建项目3. 使用方式交互式模式命令行模式编程方式使用 核心组件1. CsvService2. DatabaseService3. CsvImportService 数据类型映射性能优化1. 连接池优化2. 批量操作优化3. MySQL配置优化 配置说明application.yml…

MySQL 索引优化(Explain执行计划) 详细讲解

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 MySQL 索引优化&#xff08;Explain执行计划…

Cad 反应器 cad c#二次开发

在 AutoCAD C# 二次开发中&#xff0c;DocumentCollectionEventHandler 是一个委托&#xff08;delegate&#xff09;&#xff0c;用于处理与 AutoCAD 文档集合&#xff08;DocumentCollection&#xff09;相关的事件。它属于 AutoCAD .NET API 的事件处理机制&#xff0c;本质…

【websocket】安装与使用

websocket安装与使用 1. 介绍2. 安装3. websocketpp常用接口4. Websocketpp使用4.1 服务端4.2 客户端 1. 介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的 消息推送机制。 传统的 web 程序都是属于 “一问一答” 的形式&#xff0c;即客户端给服务器发送…

【大模型】LogRAG:基于检索增强生成的半监督日志异常检测

文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构D 实验设计D.1 数据集/评估指标D.2 SOTAD.3 实验结果 E 个人总结E.1 优点E.2 不足 A 论文出处 论文题目&#xff1a;LogRAG: Semi-Supervised Log-based Anomaly Detection with Retrieval-Augmented …

基于SpringBoot实现的大创管理系统设计与实现【源码+文档】

基于SpringBootVue实现的大创管理系统采用前后端分离架构方式&#xff0c;系统设计了管理员、学生、指导老师、院系管理员两种角色&#xff0c;系统实现了用户登录与注册、个人中心、学生管理、指导老师管理、院系管理员管理、优秀项目管理、项目类型管理、项目信息管理、项目申…

国产高云FPGA实现视频采集转UDP以太网输出,FPGA网络摄像头方案,提供2套Gowin工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目国产高云FPGA基础教程国产高云FPGA相关方案推荐我这里已有的以太网方案 3、设计思路框架工程设计原理框图输入Sensor之-->OV7725摄像头输入Sensor之-->OV5640摄…

Webpack依赖

Webpack到底怎么对我们的项目进行打包捏&#xff1f; 在webpack处理应用程序时&#xff0c;会根据命令或者配置文件找到入口文件 从入口开始&#xff0c;会生成一个依赖关系图&#xff0c;这个依赖关系图会包含应用程序中所需的所有模块&#xff08;.js、css文件、图片、字体…

自动驾驶科普(百度Apollo)学习笔记

1. 写在前面 在过去的几年里&#xff0c;自动驾驶技术取得飞速发展&#xff0c;人类社会正逐渐走向一个新时代&#xff0c;这个时代中&#xff0c;汽车不仅仅是一个交通工具&#xff0c;更是一个智能的、能够感知环境、做出决策并自主导航的机器伙伴。现在正好也从事这块的工作…

leetcode_66.加一

题目链接 这道题归类在力扣的数学类中&#xff0c;应该算是一道思维的简单题吧 题是这样的&#xff0c;根据题目我们不难理解&#xff0c;这个题就是在最后一位加 1 然后返回&#xff0c;正如示例所说的那样&#xff0c;当然这很符合我们人的思维&#xff0c;写这种算法题最重要…

iview-admin静态资源js按需加载配置

iview-admin2.0版本默认加载所有组件的JS&#xff0c;实际情况下&#xff0c;用户访问后台并不会每个页面都浏览。这样就会造成流量及带宽的浪费。可通过修改配置文件vue.config.js来实现按需加载&#xff0c;具体配置如图 image © 著作权归作者所有,转载或内容合作请联系…

抖去推--短视频矩阵系统源码开发

一、开发短视频矩阵系统的源码需要以下步骤&#xff1a; 确定系统需求&#xff1a; 根据客户的具体业务目标&#xff0c;明确系统需实现的核心功能模块&#xff0c;例如用户注册登录、视频内容上传与管理、多维度视频浏览与推荐、用户互动&#xff08;评论、点赞、分享&#xf…

Vue部署到Nginx上及问题解决

一、Vue打包 dist文件即打包文件 二、下载Nginx&#xff0c;将dist内容全部复制到Nginx的html下 三、修改Nginx的nginx.conf配置文件&#xff0c;添加try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html; 是 Nginx 配置中的一个重要指令&#xff0c;用于处理…

项目-- Json-Rpc框架

目录 项目简介环境搭建Ubuntu-22.04 第三方库使用JsonCppMuduo基础类EventLoop类TcpConnection类Buffer类TcpClient类TcpServer类 服务端基本搭建客户端基本搭建 future 项目设计通用模块设计Rpc功能模块设计发现者设计提供者设计服务注册中心设计 Topic功夫模块设计主题管理中…

因泰立科技H1X激光雷达:因泰立科技为智慧工业注入新动力

在当今工业领域&#xff0c;精准测量与高效作业是推动产业升级的关键因素。因泰立科技推出的H1X三维轮廓扫描激光雷达&#xff0c;凭借其卓越的性能和广泛的应用场景&#xff0c;正成为智慧工业中不可或缺的高科技装备。 产品简介 H1X三维轮廓扫描激光雷达是因泰立科技基于二维…

python:Tkinter 开发邮件客户端,能编写邮件,发送邮件带附件

Python Tkinter 邮件客户端 下面是一个使用 Python Tkinter 开发的简单邮件客户端&#xff0c;支持编写邮件和发送邮件功能&#xff1a; 功能说明 这个邮件客户端包含以下功能&#xff1a; 邮件编写功能&#xff1a; 收件人地址输入抄送地址输入邮件主题输入邮件正文编辑区&…

深入解析Java21核心新特性(虚拟线程,分代 ZGC,记录模式模式匹配增强)

文章目录 前言一、虚拟线程 (Virtual Threads - JEP 444) - 并发的革命1.1 解决的核心问题&#x1f3af;1.2 工作原理与核心机制⚙️1.3 使用详解与最佳实践&#x1f6e0;️1.4 注意事项⚠️1.5 总结 &#x1f4da; 二、分代 ZGC (Generational ZGC - JEP 439) - 低延迟新高度2…

免费批量去水印工具 - 针对文心一言生成图片

免费批量去水印工具 - 针对文心一言生成图片 工具介绍 这是一款免费的批量去水印工具&#xff0c;专门针对文心一言生成的图片进行处理。通过简单的操作&#xff0c;您可以快速去除图片中的水印。 下载链接 您可以通过以下网盘链接下载工具&#xff1a; 链接: https://pa…