SpringAI实现AI应用-使用redis持久化聊天记忆
SpringAI实战链接
1.SpringAl实现AI应用-快速搭建-CSDN博客
2.SpringAI实现AI应用-搭建知识库-CSDN博客
3.SpringAI实现AI应用-内置顾问-CSDN博客
4.SpringAI实现AI应用-使用redis持久化聊天记忆-CSDN博客
5.SpringAI实现AI应用-自定义顾问(Advisor)-CSDN博客
概述
针对SpringAI的内置顾问,上篇帖子已经进行了说明,这里就不再赘述,之前使用SpringAI的内置的聊天记忆顾问时,都是使用内存的方式进行存储,当项目重启的时候,聊天记录就没有了。此篇就使用redis将聊天记录进行持久化
项目修改
通过前面几篇帖子,已经有了一个项目框架,这里不再说项目搭建所需的环境,只在原来的项目上进行修改
安装并启动redis
redis的安装,网上有很多方法,在此不再说明,安装完成之后,启动redis就可以了
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>SpringAI_Demo</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.5</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>17</java.version><spring-ai.version>1.0.0-M6</spring-ai.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><!-- 常规jar--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- springAI--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><!-- 向量存储引擎--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-transformers-spring-boot-starter</artifactId></dependency><!-- 向量库--><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency><!-- 文档解析器--><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-tika-document-reader</artifactId></dependency><!-- lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><!--所在的目录--><includes><!--包括目录下的.properties,.xml 文件都会被扫描到--><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.2.5</version></plugin></plugins></build><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>
</project>
与前文相比,只添加一个redis的依赖,别的都没动
Application.yml
server:port: 3210spring:#redisdata:redis:host: 127.0.0.1port: 6379datebase: 0#向量库datasource:url: jdbc:postgresql://localhost:5432/postgresusername: postgrespassword: pgsqldriver-class-name: org.postgresql.Driverai:#调用ai大模型(可使用本地化部署模型,也可以使用线上的)openai:base-url: https://api.siliconflow.cnapi-key: #你自己申请的keychat:options:model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B#调用矢量化模型embedding:transformer:onnx:modelUri: classpath:/text2vec-base-chinese/onnx/model.onnxtokenizer:uri: classpath:/text2vec-base-chinese/onnx/tokenizer.json#矢量化配置vectorstore:pgvector:index-type: HNSWdistance-type: COSINE_DISTANCEdimensions: 768
与之前代码相比,多配置了redis的相关东西
RedisConfig(redis配置文件)
添加redis依赖之后,首先对redis进行配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;/*** @Author majinzhong* @Date 2025/5/7 14:49* @Version 1.0*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> messageRedisTemplate(RedisConnectionFactory factory, Jackson2ObjectMapperBuilder builder) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 使用String序列化器作为key的序列化方式template.setKeySerializer(new StringRedisSerializer());// 对value进行序列化template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));// 设置hash类型的key和value序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));template.afterPropertiesSet();return template;}
}
ChatRedisMemory(重写ChatMemory)
通过前文可知,聊天记忆的内置顾问都有ChatMemory,想要将聊天记录持久化就需要将ChatMemory内的方法按照redis存储的方式进行重写
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.example.entity.ChatEntity;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** @Author majinzhong* @Date 2025/5/7 14:51* @Version 1.0*/
@Slf4j
@Component
public class ChatRedisMemory implements ChatMemory {private static final String KEY_PREFIX = "chat:history:";private final RedisTemplate<String, Object> redisTemplate;public ChatRedisMemory(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic void add(String conversationId, List<Message> messages) {String key = KEY_PREFIX + conversationId;List<ChatEntity> listIn = new ArrayList<>();for (Message msg : messages) {String[] strs = msg.getText().split("</think>");String text = strs.length == 2 ? strs[1] : strs[0];ChatEntity ent = new ChatEntity();ent.setChatId(conversationId);ent.setType(msg.getMessageType().getValue());ent.setText(text);listIn.add(ent);}redisTemplate.opsForList().rightPushAll(key, listIn.toArray());redisTemplate.expire(key, 30, TimeUnit.MINUTES);}@Overridepublic List<Message> get(String conversationId, int lastN) {String key = KEY_PREFIX + conversationId;Long size = redisTemplate.opsForList().size(key);if (size == null || size == 0) {return Collections.emptyList();}int start = Math.max(0, (int) (size - lastN));List<Object> listTmp = redisTemplate.opsForList().range(key, start, -1);List<Message> listOut = new ArrayList<>();ObjectMapper objectMapper = new ObjectMapper();for (Object obj : listTmp) {ChatEntity chat = objectMapper.convertValue(obj, ChatEntity.class);if (MessageType.USER.getValue().equals(chat.getType())) {listOut.add(new UserMessage(chat.getText()));} else if (MessageType.ASSISTANT.getValue().equals(chat.getType())) {listOut.add(new AssistantMessage(chat.getText()));} else if (MessageType.SYSTEM.getValue().equals(chat.getType())) {listOut.add(new SystemMessage(chat.getText()));}}return listOut;}@Overridepublic void clear(String conversationId) {redisTemplate.delete(KEY_PREFIX + conversationId);}
}
AiConfig
因为重写了ChatMemory,所以需要重新对内置顾问进行重新配置
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.*;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;import java.util.List;/*** @Author majinzhong* @Date 2025/4/28 10:34* @Version 1.0*/
@Configuration
public class AiConfig {@BeanChatClient chatClient(ChatClient.Builder builder,VectorStore vectorStore) {return builder// 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。.defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")//这里可以添加多个顾问 order(优先级)越小,越先执行// 注意:顾问添加到链中的顺序至关重要,因为它决定了其执行的顺序。每个顾问都会以某种方式修改提示或上下文,一个顾问所做的更改会传递给链中的下一个顾问。// 在此配置中,将首先执行MessageChatMemoryAdvisor,将对话历史记录添加到提示中。然后,问答顾问将根据用户的问题和添加的对话历史进行搜索,从而可能提供更相关的结果。.defaultAdvisors(//内存存储对话记忆new MessageChatMemoryAdvisor(inMemoryChatMemory()),
// new PromptChatMemoryAdvisor(inMemoryChatMemory()),// QuestionAnswerAdvisor 此顾问使用矢量存储提供问答功能,实现RAG(检索增强生成)模式
// QuestionAnswerAdvisor.builder(vectorStore).order(1).build(),// SafeGuardAdvisor是一个安全防护顾问,它确保生成的内容符合道德和法律标准。SafeGuardAdvisor.builder().sensitiveWords(List.of("色情", "暴力")) // 敏感词列表.order(2) // 设置优先级.failureResponse("抱歉,我无法回答这个问题。").build(), // 敏感词过滤失败时的响应// SimpleLoggerAdvisor是一个记录ChatClient的请求和响应数据的顾问。这对于调试和监控您的AI交互非常有用,建议将其添加到链的末尾。new SimpleLoggerAdvisor()).defaultOptions(ChatOptions.builder().topP(0.7) // 取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值为0.8.build()).build();}@BeanChatMemory inMemoryChatMemory() {return new InMemoryChatMemory();}@Beanpublic ChatMemory chatMemory(RedisTemplate<String, Object> redisTemplate) {return new ChatRedisMemory(redisTemplate);}
}
与前文相比,多配置了ChatRedisMemory
注意:为了测试要将问答顾问注释掉
RedisAiController(新建测试接口类)
import org.example.config.ChatRedisMemory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.VectorStoreChatMemoryAdvisor;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @Author majinzhong* @Date 2025/5/7 15:03* @Version 1.0*/
@CrossOrigin
@RestController
public class RedisAiController {@AutowiredChatClient chatClient;@AutowiredChatRedisMemory chatRedisMemory;/*** 持久化聊天记录* @param message* @param sessionId* @return*/@GetMapping("/ai/redisCall")public String redisCall(@RequestParam(value = "message", defaultValue = "讲个笑话") String message, @RequestParam String sessionId) {return chatClient.prompt().user(message).advisors(new MessageChatMemoryAdvisor(chatRedisMemory, sessionId, 10)).call().content().trim();}
}
代码中注入了重写的ChatMemory(ChatRedisMemory)
也在方法中重新配置了聊天记忆的内置顾问
测试
还是使用postman进行测试
查看redis储存结果
经过测试,聊天记录已经存到redis,重启项目之后,再测试
补充
VectorStoreChatMemoryAdvisor
上篇中没有对VectorStoreChatMemoryAdvisor进行测试,现在想对它进行测试时,才发现它已经被弃用了
也来简单测试一下吧,使用之前搭建知识库时创建的向量库
@AutowiredVectorStore vectorStore;/*** 检索聊天记录向量数据库* @param message* @param sessionId* @return*/@GetMapping("/ai/vectorCall")public String vectorCall(@RequestParam(value = "message", defaultValue = "讲个笑话") String message, @RequestParam String sessionId) {VectorStoreChatMemoryAdvisor vectorStoreChatMemoryAdvisor = new VectorStoreChatMemoryAdvisor(vectorStore);return chatClient.prompt().user(message).advisors(vectorStoreChatMemoryAdvisor).call().content().trim();}
经过测试可以看出VectorStoreChatMemoryAdvisor先检索之前的对话记录,然后再生成回答
问题
调用接口时,控制台报了503,(System is too busy now. Please try again later.)这是因为AI大模型被使用的人太多了,所以才出现的错误(毕竟使用的是免费的,本地部署的AI大模型不会出现这种问题)
解决方法:换一个模型就行了,直接再配置文件里修改
相关文章:

SpringAI实现AI应用-使用redis持久化聊天记忆
SpringAI实战链接 1.SpringAl实现AI应用-快速搭建-CSDN博客 2.SpringAI实现AI应用-搭建知识库-CSDN博客 3.SpringAI实现AI应用-内置顾问-CSDN博客 4.SpringAI实现AI应用-使用redis持久化聊天记忆-CSDN博客 5.SpringAI实现AI应用-自定义顾问(Advisor)…...

C#问题 加载格式不正确解决方法
出现上面问题 解决办法:C#问题 改成x86 不要选择anycpu...
VTK|结合qt创建通用按钮控制显隐(边框、坐标轴、点线面)
文章目录 增加边框BoundingBox添加addBoundingBox添加BoundingBox控制按钮点击按钮之后的槽函数 添加坐标轴增加点线面显隐控制按钮添加控制点线面显隐的按钮到三维显示界面控制面显示槽函数控制线显示槽函数控制点显示槽函数 增加边框BoundingBox 增加边框BoundingBox并通过按…...

CentOS 7.9 安装详解:手动分区完全指南
CentOS 7.9 安装详解:手动分区完全指南 为什么需要手动分区?CentOS 7.9 基本分区说明1. /boot/efi 分区2. /boot 分区3. swap 交换分区4. / (根) 分区 可选分区(进阶设置)5. /home 分区6. /var 分区7. /tmp 分区 分区方案建议标准…...
在过滤器中获取body中的json数据并且使得后续的controller层也能获取使用
前景提示: ①我需要在filter中获取到json数据->对key名首字母进行排序,然后拼接,进行验签 ②所以就需要在filer获取到json的数据,因为请求数据是一次性读取的流。如果过滤器中调用了request.json或request.get_json()ÿ…...

如何使用测试软件 Jmeter
第一步,点击 编辑 添加线程组 第二步,右键单击线程组,添加取样器 HTTP 请求 第三步,设置请求路径 第四步,添加 查看结果树 用于查看请求响应 最后点击绿色小三角启动即可...

2025盘古石初赛WP
来不及做,还有n道题待填坑 文章目录 手机取证 Mobile Forensics分析安卓手机检材,手机的IMSI是? [答案格式:660336842291717]养鱼诈骗投资1000,五天后收益是? [答案格式:123]分析苹果手机检材&a…...

系统分析与设计期末复习
第一章 系统的五个特性 整体性、目的性、相关性、环境适应性、层次性 软件系统的四个特性 复杂性、一致性、可变性、不可见性 第二章 系统规划 系统开发生命周期 系统规划->系统分析->系统设计->系统实施->系统运行维护->系统规划 诺兰阶段模型 阶段&a…...
最大公约数gcd和最小公倍数lcm
一、相关公式及其性质 文章只服务于竞赛,所以不会涉及证明。 辗转相除法:gcd(a, b) gcd(b, a % b); 直到 b 0,就可以知道上一层递归中的 a % b 0,所以上一层的 b 就是答案,也就是这一层递归的 a gcd(a, b) * lcm…...

IBM BAW(原BPM升级版)使用教程第八讲
续前篇! 一、流程开发功能模块使用逻辑和顺序 前面我们已经对 流程、用户界面、公开的自动化服务、服务、事件、团队、数据、性能、文件各个模块进行了详细讲解,现在统一进行全面统一讲解。 在 IBM Business Automation Workflow (BAW) 中,…...
视觉革命来袭!ComfyUI-LTXVideo 让视频创作更高效
探索LTX-Video 支持的ComfyUI 在数字化视频创作领域,视频制作效果的提升对创作者来说无疑是一项重要的突破。LTX-Video支持的ComfyUI便是这样一款提供自定义节点的工具集,它专为改善视频质量、提升生成速度而开发。接下来,我们将详细介绍其功…...

从电动化到智能化,法雷奥“猛攻”中国汽车市场
当前,全球汽车产业正在经历前所未有的变革,外资Tier1巨头开始向中国智能电动汽车市场发起新一轮“猛攻”。 在4月23日-5月2日上海国际车展期间,博世、采埃孚、大陆集团、法雷奥等全球百强零部件厂商纷纷发布战略新品与转型计划。在这其中&am…...

鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用
鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用 一、创建ArkTS工程二、ArkTS工程目录结构(Stage模型)三、构建第一个页面四、构建第二个页面五、实现页面之间的跳转六、模拟器运行 一、创建ArkTS工程 1、若首次打开DevEco Studio,请点击…...

word换行符和段落标记
换行符:只换行不分段 作用:我们需要对它进行分段,但它是一个信息群组,我希望它们有同样的段落格式! 快捷键:shiftenter 段落标记:分段 快捷键:enter 修改字体格式或段落格式 …...

AI时代的数据可视化:未来已来
你有没有想过,数据可视化在未来会变成什么样?随着人工智能(AI)的飞速发展,数据可视化已经不再是简单的图表和图形,而是一个充满无限可能的智能领域。AI时代的可视化不仅能自动解读数据,还能预测…...
spark基本介绍
Spark 是基于内存计算的分布式大数据处理框架,由加州大学伯克利分校 AMPLab 开发,现已成为 Apache 顶级项目。以下是其核心要点: 核心特点 1. 内存计算:数据可驻留内存,大幅提升迭代计算(如机器学习、图计算…...

深入理解 TCP:重传机制、滑动窗口、流量控制与拥塞控制
TCP(Transmission Control Protocol)是一个面向连接、可靠传输的协议,支撑着绝大多数互联网通信。在实现可靠性的背后,TCP 引入了多个关键机制:重传机制、滑动窗口、流量控制 和 拥塞控制。这些机制共同协作࿰…...
MySQL 窗口函数入门到精通
目录 常用窗口函数速查表 1. 什么是"窗口"(不是你想的那种窗口) "窗口"≠电脑界面的窗口 那么,SQL 中的"窗口"是什么? 用表格形式理解"窗口"概念 2. 窗口函数解决了什么问题 场景…...

uniapp-商城-51-后台 商家信息(logo处理)
前面对页面基本进行了梳理和说明,特别是对验证规则进行了阐述,并对自定义规则的兼容性进行了特别补充,应该说是干货满满。不知道有没有小伙伴已经消化了。 下面我们继续前进,说说页面上的logo上传组件,主要就是uni-fil…...
✍️【TS类型体操进阶】挑战类型极限,成为类型魔法师!♂️✨
哈喽类型战士们!今天我们要玩转TS类型体操,让你的类型系统像体操运动员一样灵活优雅~ 学会这些绝招,保准你的代码类型稳如老狗!(文末附类型体操段位表)🚀 一、什么是类型体操? &…...
联邦学习图像分类实战:基于FATE与PyTorch的隐私保护机器学习系统构建指南
引言 在数据孤岛与隐私保护需求并存的今天,联邦学习(Federated Learning)作为分布式机器学习范式,为医疗影像分析、金融风控、智能交通等领域提供了创新解决方案。本文将基于FATE框架与PyTorch深度学习框架,详细阐述如…...

springboot 加载 tomcat 源码追踪
加载 TomcatServletWebServerFactory 从 SpringApplication.run()方法进入 进入到 refresh () 方法 选择实现类 ServletWebServerApplicationContext 进入到 AbstractApplicationContext onRefresh() 方法创建容器 找到加载bean 得到 webServer 实例 点击 get…...
AI实战笔记(1)AI 的 6 大核心方向 + 学习阶段路径
一、机器学习(ML) 目标:用数据“训练”模型,完成分类、回归、聚类等任务。 学习阶段: (1)基础数学:线性代数、概率统计、微积分(适度) (2…...

使用countDownLatch导致的线程安全问题,线程不安全的List-ArrayList,线程安全的List-CopyOnWriteArrayList
示例代码 package com.example.demo.service;import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class UnSafeCDTest {Executor…...

C++ - 仿 RabbitMQ 实现消息队列(1)(环境搭建)
C - 仿 RabbitMQ 实现消息队列(1)(环境搭建) 什么是消息队列核心特点核心组件工作原理常见消息队列实现应用场景优缺点 项目配置开发环境技术选型 更换软件源安装一些工具安装epel 软件源安装 lrzsz 传输工具安装git安装 cmake安装…...
66、微服务保姆教程(九)微服务的高可用性
微服务的高可用性与扩展 服务的高可用性 集群搭建与负载均衡。服务的故障容错与自愈。分布式事务与一致性 分布式事务的挑战与解决方案。使用 RocketMQ 实现分布式事务。微服务的监控与可观测性 metrics 和日志的收集与分析。sentinel 的监控功能。容器化与云原生 将微服务部署…...

RK3568-OpenHarmony(1) : OpenHarmony 5.1的编译
概述: 本文主要描述了,如何在ubuntu-20.04操作系统上,编译RK3568平台的OpenHarmony 5.1版本。 搭建编译环境 a. 安装软件包 sudo apt-get install git-lfs ruby genext2fs build-essential git curl libncurses5-dev libncursesw5-dev openjdk-11-jd…...

eFish-SBC-RK3576工控板外部RTC测试操作指南
备注: 1)测试时一定要接电池,否则外部RTC断电后无法工作导致测试失败; 2)如果连接了网络,系统会自动同步NTP时钟,所以需要关闭自动同步时钟。 关闭自动同步NTP时钟方法: 先查看是…...

vue3的深入组件-组件 v-model
组件 v-model 基本用法 v-model 可以在组件上使用以实现双向绑定。 从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏: <script setup> const model defineModel()function update() {model.value } </script><template>…...

【MySQL】数据库、数据表的基本操作
个人主页:Guiat 归属专栏:MySQL 文章目录 1. MySQL基础命令1.1 连接MySQL1.2 基本命令概览 2. 数据库操作2.1 创建数据库2.2 查看数据库2.3 选择数据库2.4 修改数据库2.5 删除数据库2.6 数据库备份与恢复 3. 表操作基础3.1 创建表3.2 查看表信息3.3 创建…...