MyBatisPlus——学习笔记

news2024/11/30 10:33:13

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

二、基本用法

示例表结构:

  1. 实体类User
     @Data
     @AllArgsConstructor
     @NoArgsConstructor
     public 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;
     }
     ​
  1. 创建UserMapper

    传统的MyBatis的方式来做,我们需要创建Mapper层并且创建对应的Mapper.xml文件,而在适用MyBatisPlus时,我们只需要写一个mapper接口继承plus提供的BaseMapper接口,这个接口包含了大部分我们平时需要用到的CRUD操作。

 @Mapper
 public interface UserMapper extends BaseMapper<User> {  // 这里User是实体类。本文都以User这个实体类为例
 ​
     // 继承baseMapper,已经有了基本的增删改查方法
 }
 ​

通过查看BaseMapper的源码,我们可以看出,大部分常用的操作已经被封装

  1. 开启扫描

为了能够让SpringBoot扫描并且识别此组件,我们需要在SpringBoot启动类上开启Mapper接口 扫描功能,添加@MapperScan()注解

 //开启扫描,注意包名不要写错
 @MapperScan("com.qf.mybatisplusdemo.mapper")
 @SpringBootApplication
 public class MyBatisPlusDemoApplication {
 public static void main(String[] args) {
 SpringApplication.run(MyBatisPlusDemoApplication.class, args);
   }
 }
 ​
  1. 测试
    此时,我们算是已经实现了基本的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;
     ​
     @SpringBootTest
     class MybatisPlusDemoApplicationTests {
         @Autowired
         private UserMapper userMapper;
         @Autowired
         private UserService userService;
         @Test
         void contextLoads() {
             // 查询全部用户
             // selectList方法参数为null,表示查询全部用户
             List<User> users = userMapper.selectList(null);
             users.forEach(System.out::println);
         }
     ​
         @Test
         public 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);
         }
     ​
         @Test
         public void  deleteBatchTest(){
           // 批量删除
             List<Long> ids = new ArrayList<>();
             ids.add(2L);
             ids.add(3L);
             int num = userMapper.deleteBatchIds(ids);
             System.out.println("删除的记录数:"+num);
         }
     ​
         @Test
         public void updateTest(){
           // 根据id去修改指定数据
             User user = new User();
             user.setId(5l);
             user.setName("Eason");
             int num = userMapper.updateById(user);
             System.out.println("更新的记录数:"+num);
         }
     ​
         @Test
         public 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实现类编写自己的业务层实现类
 @Service
 public class UserServiceImpl extends ServiceImpl<UserMapper, User>
 implements UserService {
 //自定义service方法实现
 }

测试:

 @SpringBootTest
 class MyBatisPlusDemoApplicationTests {
 @Autowired
 private UserService service;
   // 利用IService提供的saveBatch方法去进行批量插入
     @Test
     public 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 接口来实现。你需要创建一个类来实现这个接口,并在其中定义插入和更新时的填充逻辑。

  1. 实体类定义

    首先我们需要用@TableField 注解来标记哪些字段需要自动填充,并指定填充的策略.

 @TableField(fill = FieldFill.INSERT)
     private LocalDateTime createTime;
     @TableField(fill = FieldFill.INSERT_UPDATE)
     private LocalDateTime updateTime;

填充策略FieldFill枚举类型主要包括以下几个:

他们的含义分别是: 默认不处理,插入时填充字段、更新时填充字段、插入和更新时都填充字段

  1. 实现 MetaObjectHandler
 @Component
 @Slf4j
 public class MyMetaObjectHandler implements MetaObjectHandler {
     @Override
     public 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());
     }
 ​
     @Override
     public void updateFill(MetaObject metaObject) {
         log.info("start update fill ....");
         this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
     }
 }
 

七、乐观锁与分页插件

乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor 插件,使得在应用中实现乐观锁变得简单。

  1. 基本原理

    乐观锁的实现通常包括以下步骤:

    1. 读取记录时,获取当前的版本号(version)。

    2. 在更新记录时,将这个版本号一同传递。

    3. 执行更新操作时,设置 version = newVersion 的条件为 version = oldVersion

    4. 如果版本号不匹配,则更新失败。

  1. 配置乐观锁插件

首先我们需要在实体类中为version字段添加乐观锁注解@version

 @Version//乐观锁注解
 private Integer version;

然后,我们需要注册组件,也就是乐观锁拦截器,参考官网代码,我们可以创建一个MyBatisPlusConfig配置类:

我们可以将所有关于MyBatisPlus的配置放到这个配置类中,比如之前的MapperScan

@Configuration  // mybatis-plus配置类, 所有的配置都在这里
 @MapperScan("com.mybatisplusdemo.Mapper")  // 扫描mapper文件夹
 public class mybatisPlusConfig {
     /**
      * 乐观锁拦截器
      * @return
      */
     @Bean
     public MybatisPlusInterceptor mybatisPlusInterceptor() {
         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
         // 乐观锁插件
         interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
         // 分页插件
         interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
         return interceptor;
     }
 }
 ​

测试:

// 单线程测试
     @Test
     public void  mybatisPlusOptimisticLockerTest(){
         // 查询
         User user = userMapper.selectById(1L);
         // 修改用户信息
         user.setName("Eason727");
         user.setAge(28);
         // 执行更新操作
         userMapper.updateById(user);
     }
 ​
     // 多线程测试,模仿高并发场景
     @Test
     public 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 提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。

测试:

 @Test
     public 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 的逻辑删除功能会在执行数据库操作时自动处理逻辑删除字段。以下是它的工作方式:

  • 插入:逻辑删除字段的值不受限制。

  • 查找:自动添加条件,过滤掉标记为已删除的记录。

  • 更新:防止更新已删除的记录。

  • 删除:将删除操作转换为更新操作,标记记录为已删除。

逻辑删除字段支持所有数据类型,但推荐使用 IntegerBooleanLocalDateTime。如果使用 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 注解

  @TableLogic
     private Integer deleted;

我们也可以直接在实体类中设置,不用配置yaml文件

 // 逻辑删除注解 , 0表示未删除,1表示删除, 默认值为0. 这里实际上可以设置,也可以不设置    
   @TableLogic(value = "0",delval = "1") 
     private Integer deleted;

九、条件构造器

MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。

AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。此外,QueryWrapperUpdateWrapperLambdaQueryWrapperLambdaUpdateWrapper.这个四个类分别实现了关于查询、更新条件的封装以及对应的具有Lambda语法的查询、更新条件封装。

一些基本用法:​

@SpringBootTest
 public class WrapperTest {
     @Autowired
     private UserMapper userMapper;
 ​
     @Test
     void 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的用户
     @Test
     void selectTest2(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
       // 查询条件: name为Eason
         wrapper.eq("name","Eason");
         User user = userMapper.selectOne(wrapper);  // selectOne方法返回查询得到的一条数据
         System.out.println(user);
     }
 ​
     // 查询年龄在20-30之间的用户
     @Test
     void selectTest3(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
       // 查询条件: age在20-25之间
         wrapper.between("age",20,25);
         userMapper.selectList(wrapper).forEach(System.out::println);
     }
 ​
     // 模糊查询, 查询名字中不包含e的用户. like就是包含,notLike就是不包含
 ​
     @Test
     void selectTest4(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
       // 查询条件: name不包含e
         wrapper.notLike("name","e");
         userMapper.selectList(wrapper).forEach(System.out::println);
     }
 ​
     // 模糊查询,包含左侧,或者右侧
     @Test
     void selectTest5(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
         // 查询条件: name的右侧包含e
         wrapper.likeRight("name","e");
         userMapper.selectList(wrapper).forEach(System.out::println);
     }
 ​
     // 查询用户名中包含e,年龄大于20或者邮箱为null的用户
     @Test
     void selectTest6(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
       // 查询条件: name包含e,年龄大于20或者邮箱为null
         wrapper.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中,也就是可以通过它来进行判断
     @Test
     void 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执行修改和删除操作----------------------------------
     // 修改用户信息
      @Test
     void updateTest1(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
         wrapper.eq("id",6L);
         User user = new User();
         user.setName("ChanEx");
         // 根据条件更新用户信息
         userMapper.update(user,wrapper);
     }
 ​
     // 删除用户信息
     @Test
     void deleteTest1(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
         wrapper.eq("id",6L);
         // 根据条件删除用户信息
         userMapper.delete(wrapper);
     }
     //-------------------------------UpdateWrapper--------------------------------------------
     // 修改年龄大于26,且name为theshy的用户邮箱为19999@163.com
     @Test
     public 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就是不包含
     @Test
     void 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);
     }
 ​
     @Test
     void 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的用户信息
     @Test
     void selectTest10(){
         QueryWrapper<User> wrapper = new QueryWrapper<>();
         // inSql方法可以传入一个子查询, 可以用于表关联查询
         wrapper.inSql("id","select id from user where id = 7");
         userMapper.selectObjs(wrapper).forEach(System.out::println);
     }
 }

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

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

相关文章

C++基础(7)——STL简介及string类

目录 1.STL简介 1.1什么是 1.2STL的历史版本 1.3STL的六大组件 ​编辑 1.4有用的网址 2.string类 2.1string的多种定义方式 2.2string的插入 2.2.1尾插&#xff08;push_back&#xff09; 2.2.2insert插入 2.3拼接&#xff08;append&#xff09; 2.4删除 2.4.1尾…

CoRL 2024 麻省理工学院提出T3触觉Transformer,打破触觉感知的壁垒,重塑未来机器人

在智能机器人领域&#xff0c;触觉感知的研究正逐渐成为关注的焦点。然而&#xff0c;如何让机器人通过触觉更智能地感知和操作&#xff0c;依然是一个未解决的挑战。基于相机的触觉感知是一种通过在软弹性体下嵌入相机来捕获与环境的细粒度交互的感知方法&#xff0c;是最流行…

Java报错输出的信息究竟是什么?

Java报错输出的信息究竟是什么&#xff1f; 本篇会带大家了解一下java运行时报错输出的信息内容&#xff0c;简单学习一下虚拟机内存中Java虚拟机栈的工作方式以及栈帧中所存储的信息内容 异常信息 当你的程序运行报错时&#xff0c;你是否会好奇打印出来的那一大坨红色的究竟…

搜索引擎相关的一段实习经历

0 前言 就是跟搜索相关的一段经历。主要工作就是建立倒排索引库相关的一些简单内容。 又翻到了以前的工作&#xff0c;权作纪念。 就是简单的封装cpp的库供python语言调用。 反正就是很多版本问题等等吧各种鬼问题。 我感觉这个思路可能还是待考证。 跨语言的调用我感觉还是不…

泛型编程--模板【C++提升】(特化、类属、参数包的展开、static、模板机制、重载......你想知道的全都有)

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; C系列语法知识_Stark、的博客-CSDN博客 其它专栏&#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 C系列项目实战_Stark、的博客-CSDN博客 座右铭&#xff1a;梦…

Java中的while和do...while循环

while和do...while循环 while循环基本语法执行流程注意事项练习 do...while循环基本语法说明流程图练习 while循环 基本语法 循环变量初始化; while(循环条件){循环体&#xff08;语句&#xff09;;循环变量迭代; }1&#xff09;while循环也有四要素&#xff1a;循环变量初始…

【JNI】普通类型的基本使用

简单使用 在上一期我们介绍了JNI的基本使用&#xff0c;这里简单介绍一下普通类型 HelloJNI.java&#xff1a;这里计算两个整型数的平均值&#xff0c;返回值类型为double public class HelloJNI { static {System.loadLibrary("hello"); }private native String …

electron-builder 首次执行报错问题解决

假日想研究一下 react electron 的使用&#xff0c;结果发现首次打包疯狂报错&#xff0c;研究了一下之后才发现是第一次的话 electron-builder 会从外面下载依赖包到我们系统中&#xff0c;由于某种力量导致压缩包无法下载或者是下载过慢导致失败&#xff0c;要解决其实也简单…

认知战认知作战:2024年9月30日中国股市大涨背景下的认知战分析报告

认知战认知作战&#xff1a;2024年9月30日中国股市大涨背景下的认知战分析报告 关键词&#xff1a;认知战, 中国股市, 信息操纵, 心理战, 技术战, 信息监管, 投资者素养, 国际合作, 法律法规, 协同作战, 谣言澄清, 市场情绪,认知作战,新质生产力,人类命运共同体,认知战,认知域…

《Linux从小白到高手》理论篇:深入理解Linux的计划任务/定时任务

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。本篇详细深入介绍Linux的计划任务/定时计划。 Linux的计划任务 在很多时候为了自动化管理系统&#xff0c;我们都会用到计划任务&#xff0c;比如关机&#xff0c;重启&#xff0c;备份之类…

二叉树--堆

1.二叉树的顺序结构 普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储&#xff0c;需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两…

CSP-J Day 4 模拟赛补题报告

姓名&#xff1a;王胤皓&#xff0c;校区&#xff1a;和谐校区&#xff0c;考试时间&#xff1a; 2024 2024 2024 年 10 10 10 月 4 4 4 日 9 : 00 : 00 9:00:00 9:00:00~ 12 : 30 : 00 12:30:00 12:30:00&#xff0c;学号&#xff1a; S 07738 S07738 S07738 请关注作者的…

Windows应用开发-解析AVI视频文件

本Windows应用解析AVI视频文件&#xff0c;以表格的方式显示AVI文件结构。并可以将结果保存到bmp图片。下面是&#xff0c;使用该应用解析一部AVI电影获得的图片。 应用开发信息 定义一个INFO结构&#xff0c;包含两个字符串对象&#xff0c;一个ULONGLONG变量&#xff0c;和…

奔驰AMG GT50升级原厂阀门运动排气声浪效果

AMG 排气系统 声浪级别可控制的AMG高性能排气系统可带来不同凡响的听觉体验。借助可调式废气风门&#xff0c;按下按钮&#xff0c;即可按需改变车辆的声浪&#xff0c;体验不同音色。静谧深沉或动感澎湃&#xff0c;悦耳声浪&#xff0c;如你所愿。

Python画笔案例-076 绘制纯画笔弹球

1、绘制纯画笔弹球 通过 python 的turtle 库绘制 纯画笔弹球,如下图: 2、实现代码 绘制纯画笔弹球,以下为实现代码: """纯画笔弹球动画.py读者可以在此基础上把它修改成一个拦球游戏。步骤为,建立一个Rect类,即矩形类。然后采用按键检测,当按了键时重画…

​​Python+Matplotlib可视化简单反函数和复合函数

import numpy as np import matplotlib.pyplot as plt# 设置中文字体 plt.rcParams[font.sans-serif] [SimHei] # 用黑体显示中文 plt.rcParams[axes.unicode_minus] False # 正常显示负号# 创建图形和子图 fig, (ax1, ax2) plt.subplots(1, 2, figsize(15, 6))# 反函数示…

Line: 折线图

对北京市、天津市、上海市、重庆市的近10年人口&#xff0c;做出折线图&#xff0c;效果 参考&#xff1a;Line - Basic_line_chart - Document (pyecharts.org) 1、折线图模板 import pyecharts.options as opts from pyecharts.charts import Linex_data ["Mon"…

基于Springboot+Vue的中医院问诊系统的设计与实现 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…

内存卡数据恢复软件大揭秘,拯救你的重要数据

我们的生活中充斥着各种各样的数据&#xff0c;而内存卡作为一种常见的数据存储设备&#xff0c;承载着我们的照片、视频、文档等重要信息。然而存储在电子设备上就有可能导致数据丢失的情况。今天我们一起来探讨内存卡数据恢复的一些工具吧。 1.福晰内存卡数据恢复 连接直达…

ros2使用roscore报错

如果你在ros2中使用roscore命令&#xff0c;可能会出现报错。 即使你按照下面的命令安装python3-roslaunch&#xff0c;还是会报错。 注意&#xff0c;在ROS2中&#xff0c;已经不需要通过roscore命令启动ROS系统了。 ROS2中&#xff0c;不再需要ROS1中的roscore命令来启动一个…