MyBatisPlus——学习笔记
MyBatisPlus
一、导入依赖
<!-- MyBatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><!-- MySql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency>
我们通过在配置文件中添加日志配置,可以看到sql的信息
# 配置日志mybatis-plus.configuration.logimpl=org.apache.ibatis.logging.stdout.StdOutImpl
二、基本用法
示例表结构:
-
实体类User
@Data@AllArgsConstructor@NoArgsConstructorpublic class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;@Version // 乐观锁注解private Integer version;@TableLogic(value = "0",delval = "1") // 逻辑删除注解 , 0表示未删除,1表示删除, 默认值为0. 这里实际上可以设置,也可以不设置private Integer deleted;}
-
创建UserMapper
传统的MyBatis的方式来做,我们需要创建Mapper层并且创建对应的Mapper.xml文件,而在适用MyBatisPlus时,我们只需要写一个mapper接口继承plus提供的BaseMapper接口,这个接口包含了大部分我们平时需要用到的CRUD操作。
@Mapperpublic interface UserMapper extends BaseMapper<User> { // 这里User是实体类。本文都以User这个实体类为例// 继承baseMapper,已经有了基本的增删改查方法}
通过查看BaseMapper的源码,我们可以看出,大部分常用的操作已经被封装
-
开启扫描
为了能够让SpringBoot扫描并且识别此组件,我们需要在SpringBoot启动类上开启Mapper接口 扫描功能,添加@MapperScan()注解
//开启扫描,注意包名不要写错@MapperScan("com.qf.mybatisplusdemo.mapper")@SpringBootApplicationpublic class MyBatisPlusDemoApplication {public static void main(String[] args) {SpringApplication.run(MyBatisPlusDemoApplication.class, args);}}
-
测试
此时,我们算是已经实现了基本的MyBatisplus的配置,可以进行运用package com.mybatisplusdemo;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.mybatisplusdemo.Mapper.UserMapper;import com.mybatisplusdemo.entity.User;import com.mybatisplusdemo.service.UserService;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;@SpringBootTestclass MybatisPlusDemoApplicationTests {@Autowiredprivate UserMapper userMapper;@Autowiredprivate UserService userService;@Testvoid contextLoads() {// 查询全部用户// selectList方法参数为null,表示查询全部用户List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}@Testpublic void deleteTest(){// 根据map删除对应的数据Map<String,Object> map = new HashMap<>();map.put("name","Jone");map.put("age",18);int num = userMapper.deleteByMap(map);System.out.println("删除的记录数:"+num);}@Testpublic void deleteBatchTest(){// 批量删除List<Long> ids = new ArrayList<>();ids.add(2L);ids.add(3L);int num = userMapper.deleteBatchIds(ids);System.out.println("删除的记录数:"+num);}@Testpublic void updateTest(){// 根据id去修改指定数据User user = new User();user.setId(5l);user.setName("Eason");int num = userMapper.updateById(user);System.out.println("更新的记录数:"+num);}@Testpublic void selectTest() {// 根据map中传递的条件进行查询Map<String, Object> map = new HashMap<>();map.put("id",4L);map.put("name","Eason");List<User> users = userMapper.selectByMap(map);users.forEach(System.out::println);}}
三、主键生成策略
常见的一些主键策略有:UUID, Redis生成ID,雪花算法(snowflake)以及zookeeper。
在MyBatisplus中,主要是通过@TableId注解去设置主键生成策略。@TableId的源码如下:
@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})public @interface TableId {// 指定数据库表的主键字段名。如果不设置,MyBatis-Plus 将使用实体类中的字段名作为数据库表的主键字段名。String value() default ""; // 主键的生成策略。IdType type() default IdType.NONE;}
IdType的枚举类型定义:
在实体类中我们需要为主键字段加上这个注解,如果主键字段名为id,则可省略,比如这里我将数据库自增ID作为主键。
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;}
四、自定义CRUD
MyBatis-Plus的BaseMapper本身提供了很多通用的CRUD方法,极大的方便了我们的代码编写,当然,当我们遇到复杂的方法时,我们需要自定义一些方法,这个时候我们仍然要跟之前mybatis一样去实现sql编写。因为:MyBatis-Plus是在MyBatis的基础之上只做增强不做修改
我们需要配置mapper.xml的文件位置
# 指定mapper文件位置mybatis-plus.mapper-locations = classpath*:/mapper/**/*.xml
但是实际上这个配置有一个默认的配置路径也就是classpath:/mapper/**/.xml。一般我们都是默认这个路径,可以不再配置
至于其他的操作,我们仍然和Mybatis一致即可,这里不多记录。
五、IService接口
IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。
IService 接口中的方法命名遵循了一定的规范,如 get 用于查询单行,remove 用于删除,list 用于查询集合,page 用于分页查询,这样可以避免与 Mapper 层的方法混淆。
其实一般情况下,由于项目业务的复杂程度,我们都会使用自定义Service方法,那么这些如果我 们想即使用通用的IService接口提供的方法,又有自定义的方法的话,我们可以参考IService接口 的实现类ServiceImpl。
IService源码:
可以看到,IService接口中提供了许多默认的方法供我们使用,我们的Service接口只需要继承它即可
// 继承IService接口,已经有了基本的增删改查方法public interface UserService extends IService<User> {}
同样的,在实现类中我们仍然需要继承ServiceImpl实现类,我们可以先看一下ServiceImpl实现类的源码:
那么当我们自定义实现类时,可以根绝ServiceImpl的定义方法去写:
这里 M extends BaseMapper<T> 指的是继承了BaseMapper的Mapper接口,
T 指的是一个实体类
所以,我们的UserServiceImpl可以这么写:
//按照ServiceImpl实现类编写自己的业务层实现类@Servicepublic class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService {//自定义service方法实现}
测试:
@SpringBootTestclass MyBatisPlusDemoApplicationTests {@Autowiredprivate UserService service;// 利用IService提供的saveBatch方法去进行批量插入@Testpublic void insertBatchTest() {List<User> users = new ArrayList<>();for (int i = 0; i < 10; i++) {User user = new User();user.setName("smlz_" + i);user.setAge(20 + i);user.setEmail("1666189" + i + "@qq.com");users.add(user);}boolean flag = userService.saveBatch(users);System.out.println("批量插入结果:" + flag);}}
六、自动填充处理
对于创建时间create_time 和 修改时间update_time,我们可以通过MyBatis-plus进行自动填充处理
自动填充功能通过实现 com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
接口来实现。你需要创建一个类来实现这个接口,并在其中定义插入和更新时的填充逻辑。
-
实体类定义
首先我们需要用
@TableField
注解来标记哪些字段需要自动填充,并指定填充的策略.
@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
填充策略FieldFill枚举类型主要包括以下几个:
他们的含义分别是: 默认不处理,插入时填充字段、更新时填充字段、插入和更新时都填充字段
-
实现 MetaObjectHandler
@Component@Slf4jpublic class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}}
七、乐观锁与分页插件
乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor
插件,使得在应用中实现乐观锁变得简单。
-
基本原理
乐观锁的实现通常包括以下步骤:
-
读取记录时,获取当前的版本号(version)。
-
在更新记录时,将这个版本号一同传递。
-
执行更新操作时,设置
version = newVersion
的条件为version = oldVersion
。 -
如果版本号不匹配,则更新失败。
-
-
配置乐观锁插件
首先我们需要在实体类中为version字段添加乐观锁注解@version
@Version//乐观锁注解private Integer version;
然后,我们需要注册组件,也就是乐观锁拦截器,参考官网代码,我们可以创建一个MyBatisPlusConfig配置类:
我们可以将所有关于MyBatisPlus的配置放到这个配置类中,比如之前的MapperScan
@Configuration // mybatis-plus配置类, 所有的配置都在这里@MapperScan("com.mybatisplusdemo.Mapper") // 扫描mapper文件夹public class mybatisPlusConfig {/*** 乐观锁拦截器* @return*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加return interceptor;}}
测试:
// 单线程测试@Testpublic void mybatisPlusOptimisticLockerTest(){// 查询User user = userMapper.selectById(1L);// 修改用户信息user.setName("Eason727");user.setAge(28);// 执行更新操作userMapper.updateById(user);}// 多线程测试,模仿高并发场景@Testpublic void mybatisPlusOptimisticLockerTest2(){// 查询User user = userMapper.selectById(1L);// 修改用户信息user.setName("Eason1111");user.setAge(28);// 模拟另外一个线程执行了插队操作, 线程2插队---------------------User user2 = userMapper.selectById(1L);user2.setName("Eason2222");user2.setAge(22);userMapper.updateById(user2);// 执行更新操作userMapper.updateById(user);}
在单线程测试中,并不会受到影响,而在多线程测试中,由于第一个线程还未结束就开始了第二个线程,这样会导致第一个线程并没有得到执行。
MyBatis-Plus 的分页插件 PaginationInnerInterceptor
提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。
测试:
@Testpublic void pageTest(){// 简单分页模型// current:当前页 size:每页显示的记录数Page<User> page = new Page<>(2,5);userMapper.selectPage(page,null);// 获取记录List<User> users = page.getRecords();users.forEach(System.out::println);// 获取总页数System.out.println("总页数:"+page.getPages());// 获取总记录数System.out.println("总记录数:"+page.getTotal());// 获取当前页System.out.println("当前页:"+page.getCurrent());// 上一页System.out.println("是否有上一页:"+page.hasPrevious());// 下一页System.out.println("是否有下一页:"+page.hasNext());}
八、逻辑删除
逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。MyBatis-Plus 提供了便捷的逻辑删除支持,使得这一策略的实施变得简单高效。
逻辑删除的工作原理
MyBatis-Plus 的逻辑删除功能会在执行数据库操作时自动处理逻辑删除字段。以下是它的工作方式:
-
插入:逻辑删除字段的值不受限制。
-
查找:自动添加条件,过滤掉标记为已删除的记录。
-
更新:防止更新已删除的记录。
-
删除:将删除操作转换为更新操作,标记记录为已删除。
逻辑删除字段支持所有数据类型,但推荐使用 Integer
、Boolean
或 LocalDateTime
。如果使用 datetime
类型,可以配置逻辑未删除值为 null
,已删除值可以使用函数如 now()
来获取当前时间。
使用方法:
步骤 1: 配置全局逻辑删除属性
在 application.yml
中配置 MyBatis-Plus 的全局逻辑删除属性:
mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局逻辑删除字段名logic-delete-value: 1 # 逻辑已删除值logic-not-delete-value: 0 # 逻辑未删除值
步骤 2: 在实体类中使用 @TableLogic
注解
@TableLogicprivate Integer deleted;
我们也可以直接在实体类中设置,不用配置yaml文件
// 逻辑删除注解 , 0表示未删除,1表示删除, 默认值为0. 这里实际上可以设置,也可以不设置 @TableLogic(value = "0",delval = "1") private Integer deleted;
九、条件构造器
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。此外,QueryWrapper、UpdateWrapper、LambdaQueryWrapper、LambdaUpdateWrapper.这个四个类分别实现了关于查询、更新条件的封装以及对应的具有Lambda语法的查询、更新条件封装。
一些基本用法:
@SpringBootTestpublic class WrapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid selectTest1(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: name不为空,age大于20,email不为空wrapper.isNotNull("name").ge("age",20).isNotNull("email");List<User> users = userMapper.selectList(wrapper);// 根据条件查询用户users.forEach(System.out::println);}// 查询名字为eason的用户@Testvoid selectTest2(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: name为Easonwrapper.eq("name","Eason");User user = userMapper.selectOne(wrapper); // selectOne方法返回查询得到的一条数据System.out.println(user);}// 查询年龄在20-30之间的用户@Testvoid selectTest3(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: age在20-25之间wrapper.between("age",20,25);userMapper.selectList(wrapper).forEach(System.out::println);}// 模糊查询, 查询名字中不包含e的用户. like就是包含,notLike就是不包含@Testvoid selectTest4(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: name不包含ewrapper.notLike("name","e");userMapper.selectList(wrapper).forEach(System.out::println);}// 模糊查询,包含左侧,或者右侧@Testvoid selectTest5(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: name的右侧包含ewrapper.likeRight("name","e");userMapper.selectList(wrapper).forEach(System.out::println);}// 查询用户名中包含e,年龄大于20或者邮箱为null的用户@Testvoid selectTest6(){QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询条件: name包含e,年龄大于20或者邮箱为nullwrapper.like("name","e").and(wq->wq.ge("age",20).or().isNull("email"));userMapper.selectList(wrapper).forEach(System.out::println);}// 模糊查询, 查询名字中不包含e的用户. like就是包含,notLike就是不包含// 这里尝试了添加Condition条件//我们在写项目的时候,所有的条件都是由用户进行传递的,那么有的时候就无法避免参数出现空//值null的情况,所以我们应该要做一些判断,其实很多方法都提供了boolean condition这个参// 数,表示该条件是否加入最后生成的sql中,也就是可以通过它来进行判断@Testvoid selectTest7(){// 假设用户传递了参数String name = "e";Integer age = null;QueryWrapper<User> wrapper = new QueryWrapper<>();// 如果name不为空,就根据name模糊查询wrapper.like(StringUtils.isNotBlank(name),"name",name).orderByAsc(age!=null,"age"); // 如果age不为空,就根据age升序排列userMapper.selectList(wrapper).forEach(System.out::println);}// -------------------------QueryWrapper执行修改和删除操作----------------------------------// 修改用户信息@Testvoid updateTest1(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("id",6L);User user = new User();user.setName("ChanEx");// 根据条件更新用户信息userMapper.update(user,wrapper);}// 删除用户信息@Testvoid deleteTest1(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("id",6L);// 根据条件删除用户信息userMapper.delete(wrapper);}//-------------------------------UpdateWrapper--------------------------------------------// 修改年龄大于26,且name为theshy的用户邮箱为19999@163.com@Testpublic void updateTest2() {UpdateWrapper<User> wrapper = new UpdateWrapper<>();wrapper.gt("age",26).eq("name","theshy").set("email","19999@163.com");userMapper.update(null,wrapper); // 第一个参数为null,表示更新所有符合条件的记录}// -------------------------------LambdaQueryWrapper&LambdaUpdateWrapper-----------------------//它们两个的主要目的是为了防止我们在编写的时候,字段名称编写错误,我们可以直接通过//Lambda的方式来直接获取指定字段对应的实体类对应的名称 // 模糊查询, 查询名字中不包含e的用户. like就是包含,notLike就是不包含@Testvoid selectTest9(){// 假设用户传递了参数String name = "e";Integer age = null;LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.like(StringUtils.isNotBlank(name),User::getName,name).orderByAsc(age!=null,User::getAge); // 如果age不为空,就根据age升序排列userMapper.selectList(wrapper).forEach(System.out::println);}@Testvoid updateTest3(){LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();wrapper.gt(User::getAge,26).eq(User::getName,"theshy").set(User::getEmail,"10086@gmali.como");userMapper.update(null,wrapper);}// 通过子查询,查询id等于6的用户信息@Testvoid selectTest10(){QueryWrapper<User> wrapper = new QueryWrapper<>();// inSql方法可以传入一个子查询, 可以用于表关联查询wrapper.inSql("id","select id from user where id = 7");userMapper.selectObjs(wrapper).forEach(System.out::println);}}
相关文章:

MyBatisPlus——学习笔记
MyBatisPlus 一、导入依赖 <!-- MyBatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><!-- MySql --><de…...
运维自动化shell脚本总结
运维自动化是提升IT管理效率的关键,使用Shell脚本可以有效地实现许多日常任务的自动化。以下是一些常见的Shell脚本应用及其总结,涵盖基本概念、实用示例和最佳实践。 1. Shell脚本基础 1.1 Shell脚本定义 Shell脚本是一系列命令的集合,通…...

前端学习第三天笔记 JavaScript JavaScript的引入 数据类型 运算符 条件语句 字符串
这里写自定义目录标题 JavaScriptJavaScript引入到文件嵌入到HTML文件中引入本地独立js文件引入网络来源文件 JavaScript的注释方式嵌入在HTML文件中的注释JavaScript的输出方式数据类型原始类型(基础类型)合成类型(复合类型) 运算…...

C++教程一口气讲完!(万字讲解)( ̄y▽ ̄)╭ Ohohoho... 下
C 常量 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。 常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量,只不过常量的值在定义后不能进行修改。 …...

unity软件安装教程
目录 一、Unity Hub的安装 二、Unity Hub的基础设置 语言切换 安装默认路径 安装unity编辑器和visual Studio 申请许可证 创建新项目 Unity和Visual Studio进行绑定 一、Unity Hub的安装 打开浏览器输入以下网址:unity.cn,打开unity官网 点击下载&#x…...

[大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠
[大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠 目录 文章目录 [大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠目录0. 摘要1. 核心内容3. 创新点4. 算法模型5. 实验效果6. 重要数据与实验结论7. 推荐阅读指数:8. 推荐理由 后记 论文…...

云手机可以解决TikTok运营的哪些问题?
随着社交媒体的飞速发展,TikTok迅速崛起,成为个人和企业进行品牌宣传和内容创作的首选平台。然而,在运营TikTok账号的过程中,不少用户会遇到各种问题。本文将详细阐述云手机如何帮助解决这些问题。 1. 多账号管理的高效便捷 通过云…...

Redis基础三(redis的高级配置)
Redis进阶配置 一、Redis持久化操作 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。(Redis 数据都放在内存中。如果机器挂掉,内存的数据就不存在。所以需要做持久化,将内存中的数据保存在磁盘,…...
Telnet、SSH、RDP和VNC
Telnet、SSH、RDP和VNC都是远程访问和管理的协议或工具,它们各自具有不同的特点和适用场景。 一、基本概念与用途 Telnet 定义:一种基于命令行界面的远程管理协议,允许用户通过网络远程访问和管理计算机。用途:主要用于远程登录和…...

FiBiNET模型实现推荐算法
1. 项目简介 A031-FiBiNET模型项目是一个基于深度学习的推荐系统算法实现,旨在提升推荐系统的性能和精度。该项目的背景源于当今互联网平台中,推荐算法在电商、社交、内容分发等领域的广泛应用。推荐系统通过分析用户的历史行为和兴趣偏好,预…...

影刀RPA:Excel内容填充指令
1.实战目标 本次主要介绍影刀RPA如何操作内容相关的填充与替换指令。主要包含以下 这些指令在数据处理方面有着重要的作用,可以对数据做运算,填充,替换,实现数据格式统一,便于最终的数据分析。在操作的过程中…...

Threejs创建正多边体
上一章节实现了球体的绘制,这节来绘制多面体,包括正多面体,平面中,每条边一样长组成的图形叫正多边形,这里每个面一样,叫正多面体。如上文一样,先要创建出基础的组件,包括场景&#…...
链表——单链表
题目描述 实现一个单链表,链表初始为空,支持三种操作: (1) 向链表头插入一个数; (2) 删除第 k 个插入的数后面的数; (3) 在第 k 个插入的数后插入一个数 现在要对该链表进行 M 次操作,进行完所有操作后&am…...

【YOLO学习】YOLOv5口罩检测实战
文章目录 1. 环境配置2. 下载代码3. 安装库3.1 安装pytorch3.2 安装其他库 4. 测试5. 数据标注6. 模型训练7. 界面可视化 1. 环境配置 1. 先参考其他文章安装 Anaconda 或者 Miniconda,我安装的是 Miniconda。 2. 更换国内源,以加快速度 。可以参考下面这…...
场景题1-设计redis的key和value的原则
在设计 Redis 的 key 和 value 时,遵循一些最佳实践和设计原则可以确保系统的性能、可扩展性和易维护性。以下是设计 Redis key 和 value 时的常见原则: 1.RedisKey的设计原则 1.1.简短有意义 1)Redis 是内存数据库,key 越短&am…...

Shell-使用函数
在 Shell 脚本中,函数是由一段代码定义的,可以被重复调用。Shell 函数的定义和调用相对简单,并且它支持参数传递和返回值。错误处理在 Shell 中也非常重要,通常通过检查返回的状态码来判断是否有错误发生。 1.Shell 函数的定义和…...

Git介绍--github/gitee/gitlab使用
一、Git的介绍 1.1、学习Git的原因:资源管理 1.2、SCM软件的介绍 软件配置管理(SCM)是指通过执行版本控制、变更控制的规程,以及使用合适的配置管理软件来保证所有配置项的完整性和可跟踪性。配置管理是对工作成果的一种有效保护。 二、版本控制软件 …...

【ubuntu】【VirtualBox】VirtualBox无法加载USB移动设备的解决方法(支持U盘启动盘)
TOC 提示:测试可用 一、安装VirtualBox VirtualBox-7.1.2-164945-Win。 下载路径。 Download_Old_Builds_7_0 – Oracle VirtualBox 二、安装Oracle_VirtualBox_Extension_Pack-7.1.2 下载路径见上文。 三、安装增强功能 四、挂载USB 4.1 设置USB协议 4.2 挂…...

Koa2+mongodb项目实战1(项目搭建)
前言 在正式开始之前,需要先知道用到的东西: koa:Koa 是一个基于 Node.js 的 Web 应用框架,非常适合开发API服务,可以与前端框架(如 Vue.js、React.js)结合使用,实现前后端分离的开…...
Pyhton爬虫使用Selenium实现浏览器自动化操作抓取网页
第三方库Selenium主要是用来抓取动态生成的网页数据,有些网站的内容要下拉网页才会动态加载,特别是那些使用javaScript渲染的内容。当然Selenium还可用于自动化浏览器操作,比如编写一个自动抢火车票的python脚本,这并不难实现。接…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...