springboot+mybatisplus复习笔记

news2024/11/19 17:39:40

1.环境搭建

  1. 依赖配置

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
    </dependencies>
    
  2. mybatisplus配置

    spring:
    	datasource:
    		type: com.alibaba.druid.pool.DruidDataSource
    		driver-class-name: com.mysql.cj.jdbc.Driver
    		url: jdbc:mysql://localhost:3306/mybatisplus_db?		serverTimezone=UTC
    		username: root
    		password: 123456
    
    mybatis-plus:
    	global-config:
    		db-config:
    			table-prefix: tbl_
    

​ 数据库表创建:

create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
    id bigint(20) primary key auto_increment,
    name varchar(32) not null,
    password  varchar(32) not null,
    age int(3) not null ,
    tel varchar(32) not null
);
insert into user values(1,'Tom','tom',3,'18866668888');
insert into user values(2,'Jerry','jerry',4,'16688886666');
insert into user values(3,'Jock','123456',41,'18812345678');
insert into user values(4,'传智播客','itcast',15,'4006184000');

​ 实体类对象创建(实体类对象名要与表名对应)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
  1. 编写测试文件

    两个注解:

    • @Mapper保证在Dao接口上添加@Mapper注解,并且确保Dao处在引导类所在包或其子包中

      这样可以保证相应的实体类被注入

    • 在引导类上添加@MapperScan注解,其属性为所要扫描的Dao所在包

      这样保证了所有mapper类都被扫描到

  2. 说明:

    userDao注入的时候下面有红线提示的原因是什么?

    • UserDao是一个接口,不能实例化对象

    • 只有在服务器启动IOC容器初始化后,由框架创建DAO接口的代理对象来注入

    • 现在服务器并未启动,所以代理对象也未创建,IDEA查找不到对应的对象注入,所以提示报红

    • 一旦服务启动,就能注入其代理对象,所以该错误提示不影响正常运行。

  3. 整体架构

    image-20230624142413555

  4. 与mybatis的主要差别

    • 你会发现我们不需要在DAO接口中编写方法和SQL语句了,只需要继承BaseMapper接口即可

    • mybatisplus主要用于单表操作,如果需要多表操作还需要使用mybatis

  5. mybatisplus简介

    1. MP旨在成为MyBatis的最好搭档,而不是替换MyBatis,所以可以理解为MP是MyBatis的一套增强工具,它是在MyBatis的基础上进行开发的(mybatis-plus包含mybatis的jar包和mybatis-spring整合jar包)
    2. MP的特性:
      • 无侵入:只做增强不做改变,不会对现有工程产生影响
      • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作
      • 支持 Lambda:编写查询条件无需担心字段写错
      • 支持主键自动生成
      • 内置分页插件
      • ……

2.mp基础操作

image-20230624143008886

1.基础操作

  1. 新增

    int insert (T t)
    
    • T:泛型,新增用来保存新增数据

    • int:返回值,新增成功后返回1,没有新增成功返回的是0

    测试:

    @SpringBootTest
    class Mybatisplus01QuickstartApplicationTests {
    
        @Autowired
        private UserDao userDao;
    
        @Test
        void testSave() {
            User user = new User();
            user.setName("黑马程序员");
            user.setPassword("itheima");
            user.setAge(12);
            user.setTel("4006184000");
            userDao.insert(user);
        }
    }
    

    结果:

    image-20230624143143437

    这时候就发现新增了一条数据,但是我们没有设置id,这里缺自动设置了一个长id,用到了mp默认的雪花自增策略

    mp默认不能设置id,否则会爆异常

  2. 删除

    int deleteById (Serializable id)
    

    这里使用Serializable作为参数的原因:

    image-20230624143550601

    • MP使用Serializable作为参数类型,就好比我们可以用Object接收任何数据类型一样。表明了MP既能接受String类型的"1"也能接受integer类型的1。
  3. 修改

    int updateById(T t);
    
    • T:泛型,需要修改的数据内容,注意因为是根据ID进行修改,所以传入的对象中需要有ID属性值
    • int:返回值,修改成功后返回1,未修改数据返回0

    **说明:**修改的时候,只修改实体对象中有值的字段。实体对象中,没有值的字段保持原样

    测试:

    @SpringBootTest
    class Mybatisplus01QuickstartApplicationTests {
    
        @Autowired
        private UserDao userDao;
    
        @Test
        void testUpdate() {
            User user = new User();
            user.setId(1L);
            user.setName("Tom888");
            user.setPassword("tom888");
            userDao.updateById(user);
        }
    }
    
  4. 根据id查询

    T selectById (Serializable id)
    
    • Serializable:参数类型,主键ID的值
    • T:根据ID查询只会返回一条数据
    @SpringBootTest
    class Mybatisplus01QuickstartApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetById() {
            User user = userDao.selectById(2L);
            System.out.println(user);
        }
    }
    
  5. 查询所有(不加条件)

    List<T> selectList(Wrapper<T> queryWrapper)
    
    • **Wrapper:**用来构建条件查询的条件,目前我们没有可直接传为Null
    • List:因为查询的是所有,所以返回的数据是一个集合
  6. 总结

    我们所调用的方法都是来自于DAO接口继承的BaseMapper类中

2.lombok简单开发

  1. 配置lombok

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <!--<version>1.18.12</version>-->
    </dependency>
    

    **注意:**版本可以不用写,因为SpringBoot中已经管理了lombok的版本。

  2. 安装lombok插件

    新版本IDEA已经内置了该插件,如果删除setter和getter方法程序有报红,则需要安装插件

  3. lombok注解

    • @Setter:为模型类的属性提供setter方法
    • @Getter:为模型类的属性提供getter方法
    • @ToString:为模型类的属性提供toString方法
    • @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法
    • @Data:是个组合注解,包含上面的注解的功能
    • @NoArgsConstructor:提供一个无参构造函数
    • @AllArgsConstructor:提供一个包含所有参数的构造函数
  4. lombok注解使用说明

    @Data不包含构造方法,但JDK会自动生成无参构造,使用全参构造要加**@AllArgsConstructor,加入带参构造时自动生成的无参构造会失效所以还需要加上@NoArgsConstructor**

  5. 举例说明

    这是一个比较完整的使用lombok注解的例子

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Long id;
        private String name;
        private String password;
        private Integer age;
        private String tel;
    
        public User(String name, String password) {
            this.name = name;
            this.password = password;
        }
    }
    

3.分页查询

分页查询中我们仍然需要用到一个特殊的IPage类,不同于mybatis中用到的pageHelper

介绍:

IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
  • IPage:用来构建分页查询条件
  • Wrapper:用来构建条件查询的条件,目前我们没有可直接传为Null
  • IPage:返回值,你会发现构建分页条件和方法的返回值都是IPage

IPage是一个接口,我们需要找到它的实现类来构建它,具体的实现类,可以进入到IPage类中按ctrl+h,会找到其有一个实现类为Page

使用方法:

  1. 配置mp分页拦截器

    @Configuration
    public class MybatisPlusConfig {
        
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            //1 创建MybatisPlusInterceptor拦截器对象
            MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
            //2 添加分页拦截器
            mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return mpInterceptor;
        }
    }
    
    • 创建Page对象,设置分页条件
    • 调用basemapper中的selectPage(IPage,Query)方法设置page对象和查询条件
    • 方法查询后的数据会封装回page对象

举例:

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {

    @Autowired
    private UserDao userDao;
    
    //分页查询
    @Test
    void testSelectPage(){
        //1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
        IPage<User> page=new Page<>(1,3);
        //2 执行分页查询
        userDao.selectPage(page,null);
        //3 获取分页结果
        System.out.println("当前页码值:"+page.getCurrent());
        System.out.println("每页显示数:"+page.getSize());
        System.out.println("一共多少页:"+page.getPages());
        System.out.println("一共多少条数据:"+page.getTotal());
        System.out.println("数据:"+page.getRecords());//重要
    }
}

分页查询原理:

​ select * from * limit ?,?

​ ①先查询有多少条数据

​ ②根据分页大小得到多少页

​ ③根据当前页数和分页大小,得到limt的两个问号的参数

  • 举例:
IPage<User> page = new Page<>(2,2);     //创建分页对象
//当前页2页,每页2条数据
  • 日志打印:

image-20230624150201737

4.测试日志

  • 如果想查看MP执行的SQL语句,可以修改application.yml配置文件
mybatis-plus:
	configuration:
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台
  • 取消spring的初始打印

    测试的时候,控制台打印的日志比较多,速度有点慢而且不利于查看运行结果,所以接下来我们把这个日志处理下:

    • 取消初始化spring日志打印,resources目录下添加logback.xml,名称固定,内容如下:

      <?xml version="1.0" encoding="UTF-8"?>
      <configuration>
      </configuration>
      
    • 取消MybatisPlus启动banner图标

      # mybatis-plus日志控制台输出
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        global-config:
          banner: off # 关闭mybatisplus启动图标
      
    • 取消SpringBoot的log打印

      spring:
        main:
          banner-mode: off # 关闭SpringBoot启动图标(banner)
      

    取消完之后的控制台打印效果:

    image-20230624152234641

3.DQL编程

  • 本质上就是通过mybatis-plus实现Wrapper接口,然后按条件进行查询,这样做简化了mybatis操作的直接查询SQL语句的编写,用编程实现了查询SQL

  • 查询操作:

    1. 封装一个wrapper对象,指定相应的查询条件

    2. 调用dao层提供的通用的带有wrapper接口参数的select方法进行查询

      image-20230610212250169

  • mybatis字段匹配原则:

    实体类属性可以比数据表中字段少,但是一个实体类对应一张表。

    因此只要出现在实体类中的属性必须与数据表中的某一个字段对应,否则会出错。

    如果某属性是实体类的冗余属性(即表中没有字段对应),那么用@TableField注解的exists属性设置为false声明

1.条件查询

  1. 介绍

    • MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。

    这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper类,这个类就是用来构建查询条件的,如下图所示:

    image-20230624151724676

  2. 构建条件查询

    在进行查询的时候,我们的入口是在Wrapper这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,说明我们有多种构建查询条件对象的方式

    image-20230624152358627

    • QueryWrapper

      直接调用方法,拼接String字段,容易出现字段不匹配,不建议使用

      @SpringBootTest
      class Mybatisplus02DqlApplicationTests {
      
          @Autowired
          private UserDao userDao;
      
          @Test
          void testGetAll(){
              QueryWrapper qw = new QueryWrapper();
              //拼接条件字段
              qw.lt("age",18);
              List<User> userList = userDao.selectList(qw);
              System.out.println(userList);
          }
      }
      
    • LambdaQueryWrapper

      用lambda表达式,方法引用实体类的字段(只要实体类与表中字段一致[后面会讲到映射匹配问题,解决映射问题]),做到查询条件拼接的准确性

      @SpringBootTest
      class Mybatisplus02DqlApplicationTests {
      
          @Autowired
          private UserDao userDao;
          
          @Test
          void testGetAll(){
              LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
              lqw.lt(User::getAge, 10);
              List<User> userList = userDao.selectList(lqw);
              System.out.println(userList);
          }
      }
      
  3. 多条件构建查询:

    ①直接queryWrapper一次一次的添加

    ②链式编程,不断的.

    举例:

    //需求:查询数据库表中,年龄小于10或年龄大于30的数据
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
            //条件
            lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
            List<User> userList = userDao.selectList(lqw);
            System.out.println(userList);
        }
    }
    

    说明:

    or()就相当于我们sql语句中的or关键字,不加默认是and,最终的sql语句为:

    SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
    
  4. null值的判定:

    • 问题出现:虽然构造好了查询条件,但是并不是每次查询所有的限制条件都要用到。这种情况类似mybatis的动态SQL拼接问题

    • 举例:

      需求:查询数据库表中,根据输入年龄范围来查询符合条件的记录

      用户在输入值的时候,

      ​ 如果只输入第一个框,说明要查询大于该年龄的用户

      ​ 如果只输入第二个框,说明要查询小于该年龄的用户

      ​ 如果两个框都输入了,说明要查询年龄在两个范围之间的用户

  5. 解决前端与后端实体类不匹配问题:

    后台如果想接收前端的两个数据,该如何接收?

    **方法:**新建一个模型类,让其继承User类,并在其中添加age2属性,UserQuery在拥有User属性后同时添加了age2属性。让其充当保存查询条件功能

    总结:

    //lambda表达式泛型类一定是User,对应一张表
    //UserQuery只是作为查询条件存在

    @Data
    public class User {
        private Long id;
        private String name;
        private String password;
        private Integer age;
        private String tel;
    }
    
    //查询类
    @Data
    public class UserQuery extends User {
        private Integer age2;
    }
    
  6. 解决动态SQL拼接问题:

    使用mp重载的方法解决

    image-20230624153717232

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            //模拟页面传递过来的查询数据
            UserQuery uq = new UserQuery();
            uq.setAge(10);
            uq.setAge2(30);
            
            //lambda表达式泛型类一定是User,对应一张表
            //UserQuery只是作为查询条件存在
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
            //添加条件
            lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());
            lqw.gt(null!=uq.getAge(),User::getAge, uq.getAge());
            List<User> userList = userDao.selectList(lqw);
            System.out.println(userList);
        }
    }
    

2.查询投影

  1. 介绍:就是从数据库查询出结果后,如何封装数据集

  2. 实现结果封装:

    • 有实体类与其对应:通过warpper的select指定select (内容)设置

    • 无实体类对应:selectMaps将查询出来的结果封装到一个list的map里面,适合查询特殊的无法封装到实体类对象的东西,如count(*)

  1. 查询指定字段:

    • 默认:目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即不查询所有字段,只查询出指定内容的数据。

    • 指定:要想实现指定查询,要调用Wrapper.select()方法来实现

      举例:

      @SpringBootTest
      class Mybatisplus02DqlApplicationTests {
      
          @Autowired
          private UserDao userDao;
          
          @Test
          void testGetAll(){
              LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
              lqw.select(User::getId,User::getName,User::getAge);
              List<User> userList = userDao.selectList(lqw);
              System.out.println(userList);
          }
      }
      

      最终的sql语句为:SELECT id,name,age,tel FROM user

  2. 聚合查询

    • **查询条件:**因为不是固定的列,只能用普通的select来实现,不能用lambda形式的querywrapper,相比mybatis在封装数据集就麻烦了

    • **返回值:**查询结果的返回值,也只能用Map集合来接收,调用selectMaps()方法

    • **起别名:**为了在做结果封装的时候能够更简单,我们将聚合函数后得到的结果都起了个名称,方面后期来获取这些数据

    举例:

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            QueryWrapper<User> lqw = new QueryWrapper<User>();
            
            lqw.select("avg(age) as avgAge");
            
            //返回值也只能用Map接受,要调用selectMaps()方法
            List<Map<String, Object>> userList = userDao.selectMaps(lqw);
            System.out.println(userList);
        }
    }
    
  3. 分组查询:

    • 聚合与分组查询,无法使用lambda表达式来完成
    • MP只是对MyBatis的增强,如果MP实现不了,我们可以直接在DAO接口中使用MyBatis的方式实现
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            QueryWrapper<User> lqw = new QueryWrapper<User>();
            lqw.select("count(*) as count,tel");
            //分组查询
            lqw.groupBy("tel");
            //
            List<Map<String, Object>> list = userDao.selectMaps(lqw);
            System.out.println(list);
        }
    }
    

3.查询条件

MP的查询条件有很多:

  • 范围匹配(> 、 = 、between)
  • 模糊匹配(like)
  • 空判定(null)
  • 包含性匹配(in)
  • 分组(group)
  • 排序(order)
  • ……

查询条件官方文档参考

  1. 等值查询

    • eq(): 相当于 =,对应的sql语句为

      SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?)
      
    • selectList:查询结果为多个或者单个

    • selectOne:查询结果为单个

    //需求:根据用户名和密码查询用户信息
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
            lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
            User loginUser = userDao.selectOne(lqw);
            System.out.println(loginUser);
        }
    }
    
  2. 范围查询

    • gt():大于(>)
    • ge():大于等于(>=)
    • lt():小于(<)
    • lte():小于等于(<=)
    • between():between ? and ?
    //需求:对年龄进行范围查询,使用lt()、le()、gt()、ge()、between()进行范围查询
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
            lqw.between(User::getAge, 10, 30);
            //SELECT id,name,password,age,tel FROM user WHERE (age BETWEEN ? AND ?)
            List<User> userList = userDao.selectList(lqw);
            System.out.println(userList);
        }
    }
    
  3. 模糊查询⭐️

    • like():前后加百分号,如 %J%
    • likeLeft():前面加百分号,如 %J
    • likeRight():后面加百分号,如 J%
    //需求:查询表中name属性的值以`J`开头的用户信息,使用like进行模糊查询
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
            lqw.likeRight(User::getName, "J");
            //SELECT id,name,password,age,tel FROM user WHERE (name LIKE ?)
            List<User> userList = userDao.selectList(lqw);
            System.out.println(userList);
        }
    }
    
  4. 排序查询

    • orderBy排序
      • condition:条件,true则添加排序,false则不添加排序
      • isAsc:是否为升序,true升序,false降序
      • columns:排序字段,可以有多个
    • orderByAsc/Desc(单个column):按照指定字段进行升序/降序
    • orderByAsc/Desc(多个column):按照多个字段进行升序/降序
    • orderByAsc/Desc
      • condition:条件,true添加排序,false不添加排序
      • 多个columns:按照多个字段进行排序

    image-20230624161702241

    //需求:查询所有数据,然后按照id降序
    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();
            /**
             * condition :条件,返回boolean,
             		当condition为true,进行排序,如果为false,则不排序
             * isAsc:是否为升序,true为升序,false为降序
             * columns:需要操作的列
             */
            lwq.orderBy(true,false, User::getId);
    
            userDao.selectList(lw
        }
    }
    

4.映射匹配兼容性✏️

处理属性兼容性:返回值相当于mybatis里面的ResultMap,用来处理java实体类字段与表中字段不匹配的问题

  1. 介绍:

    image-20230624162147268

  2. 问题一:表字段与编码属性设计不同步

    image-20230624162316940

  3. 问题二:编码中添加了数据库中未定义的属性

    • 这一点与mybatis不同,mybatis即使实体类中有数据库未定义的属性,也能根据sql语句将查询出来的结果赋值给相应的实体类属性

    • mp要求实体类对象中的字段必须和表中字段一一对应,如果不对应要加相应的配置说明

    image-20230624162409335

  4. 问题三:采用默认查询开放了更多的字段查看权限

    • 查询表中所有的列的数据,就可能把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。

    • 解决方案:是@TableField注解的一个属性叫select,该属性设置默认是否需要查询该字段的值,true(默认值)表示默认查询该字段,false表示默认不查询该字段。

    • 这样处理后,不会再从数据库表中查出pwd赋值给User实体类属性

下面问题设计到表名与实体类名对应问题:

  1. 问题四:表名与编码开发设计不同步

    • 解决方案一:@TableName注解加载实体类对象上,指明表名

    • 解决方案二:

      mybatis-plus:
        global-config:
          db-config:
            #设置数据库表前缀
            table-prefix: tbl_
      
  2. 注解知识点总结:

    • @TableField

      名称@TableField
      类型属性注解
      位置模型类属性定义上方
      作用设置当前属性对应的数据库表中的字段关系
      相关属性value(默认):设置数据库表字段名称
      exist:设置属性在数据库表字段中是否存在,默认为true,此属性不能与value合并使用
      select:设置属性是否参与查询,此属性与select()映射配置不冲突
    • @TableName

      名称@TableName
      类型类注解
      位置模型类定义上方
      作用设置当前类对应于数据库表关系
      相关属性value(默认):设置数据库表名称

4.DML编程

1.id生成策略控制(针对insert)

  • 不同的表应用不同的id生成策略
    • 日志:自增(1,2,3,4,……)
    • 购物订单:特殊规则(FQ23948AK3843)
    • 外卖单:关联地区日期等信息(10 04 20200314 34 91)
    • 关系表:可省略id
    • ……

​ 不同的业务采用的ID生成方式应该是不一样的,那么在MP中都提供了哪些主键生成策略,以及我们该如何进行选择?

​ 在这里我们又需要用到MP的一个注解叫@TableId

@TableId

名称@TableId
类型属性注解
位置模型类中用于表示主键的属性定义上方
作用设置当前类中主键属性的生成策略
相关属性value(默认):设置数据库表主键名称
type:设置主键属性的生成策略,值查照IdType的枚举值
  • 举例:修改为id自增为1的自增策略

    1. 设置@TableId
    @Data
    @TableName("tbl_user")
    public class User {
        //设置生成策略为AUTO
        @TableId(type = IdType.AUTO)
        private Long id;
        private String name;
        @TableField(value="pwd",select=false)
        private String password;
        private Integer age;
        private String tel;
        @TableField(exist=false)
        private Integer online;
    }
    
    1. 删除测试数据并修改自增值

      • 修改自增值

        因为之前生成主键ID的值比较长,会把MySQL的自动增长的值变的很大,所以需要将其调整为目前最新的id值。

        image-20230624165140179

      • 一定要设置数据库表为主键id自增

  • 常见ID自增策略

    • NONE: 不设置id生成策略
    • INPUT:用户手工输入id
    • ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型)
    • ASSIGN_UUID:以UUID生成算法作为id生成策略,生成为接收类型必须为(String)类型
    • 其他的几个策略均已过时,都将被ASSIGN_ID和ASSIGN_UUID代替掉。
  • 拓展:分布式id

    分布式ID是什么?

    • 当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储
    • 比如订单表就有可能被存储在不同的服务器上
    • 如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突
    • 这个时候就需要一个全局唯一ID,这个ID就是分布式ID。
  • 常见ID自增策略介绍:

    • INPUT策略

      ①实现此种策略首先修改实体类@TableId

      ②设置数据库表为非自增

      ③用户手工输入id,如果不输入id,会爆主键为null异常

    • ASSIGN_ID

      ①实现此种策略首先修改实体类@TableId

      ②添加用户数据的时候不设置ID,如果用户输入了ID,那么以用户输入的为准

    • ASSIGN_UUID:

      ①实现此种策略首先修改实体类@TableId

      设置实体类接收id为String类型,SQL表中类型为varchar类型

      ​ 主键类型设置为varchar,长度要大于32,因为UUID生成的主键为32位,如果长度小的话就会导致插入失败

      ③添加用户数据的时候不设置ID,如果用户输入了ID,那么以用户输入的为准

  • 雪花算法:

    image-20230624170142128

  • ID生成策略对比

    • NONE: 不设置id生成策略,MP不自动生成,约等于INPUT,所以这两种方式都需要用户手动设置,但是手动设置第一个问题是容易出现相同的ID造成主键冲突,为了保证主键不冲突就需要做很多判定,实现起来比较复杂**(不推荐使用)**
    • AUTO:数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用(不推荐使用)
    • ASSIGN_UUID:可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是32位的字符串,长度过长占用空间而且还不能排序,查询性能也慢
    • ASSIGN_ID:可以在分布式的情况下使用,生成的是Long类型的数字,可以排序性能也高,但是生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键**(推荐使用,mp默认)**
    • 综上所述,每一种主键策略都有自己的优缺点,根据自己项目业务的实际情况来选择使用才是最明智的选择。
  • 简化配置

    #id自增策略
    mybatis-plus:
      global-config:
        db-config:
        	id-type: assign_id
    #表前缀
    mybatis-plus:
      global-config:
        db-config:
        	table-prefix: tbl_        
    

2.多记录操作(只针对select和delect)

为什么多记录操作没有insert和update

  • 因为批量添加的话一条insert语句是 insert into tb_user values(?,?,?),(?,?,?).......

    这样的话拼接SQL太长了

    因此在这个basemapper中只有添加一条数据的方法,要想添加多条,要么自己写一个循环方法实现

    要么实现Iservice接口,来使用里面的通用方法

  • 多id删除多id查询

    image-20230624170717563

  1. 多记录删除

    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    //需求:根据传入的id集合将数据库表中的数据删除掉。
    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testDelete(){
            //删除指定多条数据
            List<Long> list = new ArrayList<>();
            list.add(1402551342481838081L);
            list.add(1402553134049501186L);
            list.add(1402553619611430913L);
            userDao.deleteBatchIds(list);
        }
    }
    
  2. 多记录查询

    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    //需求:根据传入的ID集合查询用户信息
    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testGetByIds(){
            //查询指定多条数据
            List<Long> list = new ArrayList<>();
            list.add(1L);
            list.add(3L);
            list.add(4L);
            userDao.selectBatchIds(list);
        }
    }
    

3.逻辑删除✏️

  1. 逻辑删除出现背景:

    接下来要讲解是删除中比较重要的一个操作,逻辑删除,先来分析下问题:

    1631246806130

    • 这是一个员工和其所签的合同表,关系是一个员工可以签多个合同,是一个一(员工)对多(合同)的表

    • 员工ID为1的张业绩,总共签了三个合同,如果此时他离职了,我们需要将员工表中的数据进行删除,会执行delete操作

    • 如果表在设计的时候有主外键关系,那么同时也得将合同表中的前三条数据也删除掉

      1631246997190

    • 后期要统计所签合同的总金额,就会发现对不上,原因是已经将员工1签的合同信息删除掉了

    • 如果只删除员工不删除合同表数据,那么合同的员工编号对应的员工信息不存在,那么就会出现垃圾数据,就会出现无主合同,根本不知道有张业绩这个人的存在

    • 所以经过分析,我们不应该将表中的数据删除掉,而是需要进行保留,但是又得把离职的人和在职的人进行区分,这样就解决了上述问题,如:

      1631247188218

    • 区分的方式,就是在员工表中添加一列数据deleted,如果为0说明在职员工,如果离职则将其改完1,(0和1所代表的含义是可以自定义的)

  2. 逻辑删除原理:(假设删除为1,不删除为0)

    使用mp提供的@Logic注解表名逻辑删除字段后,用mp进行以下操作:

    • 每次在进行删除的时候,会将逻辑删除字段设置为1,不删除字段为0

    • 每次在进行修改操作时,加上不修改逻辑删除字段(为1)的条件

    • 每次在进行查询操作时,加上不修改逻辑删除字段(为1)的条件

    通过上述操作,实现了逻辑删除的效果

  3. 逻辑删除功能实现

    ①在数据库表中加上冗余字段

    ②实体类添加属性

    • 添加与数据库表的列对应的一个属性名,名称可以任意,如果和数据表列名对不上,可以使用**@TableField进行关系映射**(必须要映射上),如果一致,则会自动对应。

    • 标识新增的字段为逻辑删除字段,使用@TableLogic

      @Data
      public class User {
          ......
          @TableLogic(value="0",delval="1")
          //value为正常数据的值,delval为删除数据的值
          private Integer deleted;
      }
      

​ ③进行测试

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testDelete(){
       userDao.deleteById(1L);
    }
}

image-20230624172254965

  1. 打破逻辑删除功能

    如果还是想把已经删除的数据都查询出来该如何实现呢?

    答:使用mybatis原生SQL查询

    @Mapper
    public interface UserDao extends BaseMapper<User> {
        //查询所有数据包含已经被删除的数据
        @Select("select * from tbl_user")
        public List<User> selectAll();
    }
    
  2. 逻辑删除简化配置

    mybatis-plus:
      global-config:
        db-config:
          # 逻辑删除字段名
          logic-delete-field: deleted
          # 逻辑删除字面值:未删除为0
          logic-not-delete-value: 0
          # 逻辑删除字面值:删除为1
          logic-delete-value: 1
    

4.乐观锁✏️(针对update)

  • 数据库表中添加version列,比如默认值给1
  • 第一个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第二个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
  • 第一个线程执行更新时,set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 第二个线程执行更新时,set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 假如这两个线程都来更新数据,第一个和第二个线程都可能先执行
    • 假如第一个线程先执行更新,会把version改为2,
    • 第二个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数据version已经为2,所以第二个线程会修改失败
    • 不管谁先执行都会确保只能有一个线程更新数据,这就是MP提供的乐观锁的实现原理分析。

**总结:**说白了就是两个操作都先执行查找操作,拿到同一个版本,但是只能有一个人进行修改,因为只要有一个人修改了,版本也就跟着更新了

乐观锁注意事项:使用乐观锁更新数据,更新字段一定要带上乐观锁字段,否则无法实现

乐观锁实现步骤:

  1. **数据库表添加列,给列添加上默认值。**比如说1,记录最开始的版本

  2. 在模型类中添加对应的属性,并加上@veision注解

  3. 添加乐观锁的拦截器

    因为乐观锁实现要给sql语句加上额外语句,类似分页拦截器要加上limit…,属于额外功能,所以需要配置上拦截器

    @Configuration
    public class MpConfig {
        @Bean
        public MybatisPlusInterceptor mpInterceptor() {
            //1.定义Mp拦截器
            MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
            //2.添加乐观锁拦截器
            mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mpInterceptor;
        }
    }
    
  4. 执行更新操作:

    注意:在执行更新之前一定要查询一遍或者将乐观锁字段设置进更新字段里面,确保乐观锁生效

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testUpdate(){
           User user = new User();
            user.setId(3L);
            user.setName("Jock666");
            //设置乐观锁字段
            user.setVersion(2);
            userDao.updateById(user);
        }
    }
    

乐观锁验证举例:

最终结果是数据库表中变成"Jock bbb",version从1变成2

@Test
public void testOptimisticLock(){
    //1.先通过要修改的数据id将当前数据查询出来
    User user = userDao.selectById(3L);     //version=1
    User user2 = userDao.selectById(3L);    //version=1
    user2.setName("Jock bbb");
    userDao.updateById(user2);              //version=>2
    user.setName("Jock aaa");
    userDao.updateById(user);               //verion=2?条件还成立吗?
}

官网乐观锁

5.通用service接口

说明:

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper 为 条件构造器

使用通用service接口可以将basemapper中简单的增删改查操作逻辑化,满足业务要求

但是这个接口实现的方法都很通用,日常开发还要自己封装业务逻辑代码

官网地址

IService中的CRUD方法

  • 增加:Save、SaveOrUpdate

    // 插入一条记录(选择字段,策略插入)
    boolean save(T entity);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList, int batchSize);
    
    // TableId 注解存在更新记录,否插入一条记录
    boolean saveOrUpdate(T entity);
    // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
    boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList);
    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
    
  • 删除:Remove

    // 根据 entity 条件,删除记录
    boolean remove(Wrapper<T> queryWrapper);
    // 根据 ID 删除
    boolean removeById(Serializable id);
    // 根据 columnMap 条件,删除记录
    boolean removeByMap(Map<String, Object> columnMap);
    // 删除(根据ID 批量删除)
    boolean removeByIds(Collection<? extends Serializable> idList);
    
  • 修改:Update

    // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
    boolean update(Wrapper<T> updateWrapper);
    // 根据 whereWrapper 条件,更新记录
    boolean update(T updateEntity, Wrapper<T> whereWrapper);
    // 根据 ID 选择修改
    boolean updateById(T entity);
    // 根据ID 批量更新
    boolean updateBatchById(Collection<T> entityList);
    // 根据ID 批量更新
    boolean updateBatchById(Collection<T> entityList, int batchSize);
    
  • 查询:Get、List、Count

    // 根据 ID 查询
    T getById(Serializable id);
    // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
    T getOne(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);
    // 根据 Wrapper,查询一条记录
    Map<String, Object> getMap(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    
    
    // 查询所有
    List<T> list();
    // 查询列表
    List<T> list(Wrapper<T> queryWrapper);
    // 查询(根据ID 批量查询)
    Collection<T> listByIds(Collection<? extends Serializable> idList);
    // 查询(根据 columnMap 条件)
    Collection<T> listByMap(Map<String, Object> columnMap);
    // 查询所有列表
    List<Map<String, Object>> listMaps();
    // 查询列表
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
    // 查询全部记录
    List<Object> listObjs();
    // 查询全部记录
    <V> List<V> listObjs(Function<? super Object, V> mapper);
    // 根据 Wrapper 条件,查询全部记录
    List<Object> listObjs(Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    
    // 查询总记录数
    int count();
    // 根据 Wrapper 条件,查询总记录数
    int count(Wrapper<T> queryWrapper);
    
  • Page

    // 无条件分页查询
    IPage<T> page(IPage<T> page);
    // 条件分页查询
    IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
    // 无条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page);
    // 条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
    

6.快速开发

使用mybatisx插件实现快速开发

功能:

  • 快速生成CRUD模版,生成mapper和service
  • 对于一些需要自己编写的代码和SQL语句,比如多表联查,可以加上自己清晰表达后实现快速生成SQL语句

插件官网

1.快速生成代码

  • 新建一个Spring Boot项目引入依赖(创建工程时记得勾选lombok及mysql驱动)

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>
    
  • 配置数据源信息

    spring:
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
  • 在IDEA中与数据库建立链接

  • 填写数据库信息并保存

  • 找到我们需要生成的表点击右键

    image-20220522121613909

  • 填写完信息以后下一步

    image-20220522122127649

  • 继续填写信息

    image-20220522122525598

  • 大功告成(真特么好用yyds)

    image-20220522122612334

2.快速生成CRUD

MyBaitsX可以根据我们在Mapper接口中输入的方法名快速帮我们生成对应的sql语句

image-20220522123143852

image-20220522123202310

per queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage page, Wrapper queryWrapper);




# 6.快速开发

> 使用`mybatisx`插件实现快速开发
>
> 功能:
>
> * 快速生成CRUD模版,生成mapper和service
> * 对于一些需要自己编写的代码和SQL语句,比如多表联查,可以加上自己清晰表达后实现快速生成`SQL语句`

[插件官网](https://baomidou.com/pages/ba5b24/)

## 1.快速生成代码

- 新建一个Spring Boot项目引入依赖(创建工程时记得勾选lombok及mysql驱动)

```xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>
  • 配置数据源信息

    spring:
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
  • 在IDEA中与数据库建立链接

  • 填写数据库信息并保存

  • 找到我们需要生成的表点击右键

    [外链图片转存中…(img-nSTQncGU-1687610658206)]

  • 填写完信息以后下一步

    [外链图片转存中…(img-wZ7fmK6s-1687610658207)]

  • 继续填写信息

    [外链图片转存中…(img-5g9FqjKD-1687610658208)]

  • 大功告成(真特么好用yyds)

    [外链图片转存中…(img-QoUYwWJ9-1687610658209)]

2.快速生成CRUD

MyBaitsX可以根据我们在Mapper接口中输入的方法名快速帮我们生成对应的sql语句

[外链图片转存中…(img-6Tc55Nkg-1687610658210)]

[外链图片转存中…(img-kIO6dXPk-1687610658210)]

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

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

相关文章

【Twitter爬虫】Twitter网络爬虫

利用selenium爬取Twitter 从2月9日起&#xff0c;Twitter不再支持免费访问Twitter API&#xff0c;继续使用Twitter API支付较高的费用。下面将介绍一种绕过Twitter API爬取推文的方式 Selenium Webdriver框架 首先介绍一下Selenium Webdriver&#xff0c;这是一款web自动化…

软件工程实践总结

前言 这次我们学校花了很多心血在这次的课设上&#xff0c;真的是特别感动和感谢&#xff0c;当你遇到真心为你好对你好的老师的时候&#xff0c;真的是会觉得人间值得&#xff01; 之前在学软件工程的时候我就会觉得这些理论的东西有什么用啊&#xff0c;什么UML&#xff0c;…

冒泡排序、选择排序、插入排序

冒泡排序 思路&#xff1a; 每次循环比较相邻两个元素&#xff0c;如果左边元素>右边元素&#xff0c;则交换位置。结果&#xff1a;将最大值放到最右边&#xff1b;一次循环过后&#xff0c;左边无序区域减少一个数&#xff0c;右边有序取增加一个数&#xff1b;序列长度…

【C++】AVL树的插入实现

目录 AVL树的概念AVL树节点的定义AVL树的插入AVL树的旋转左单旋(parent->_bf 2 && cur->_bf 1)a,b,c当高度为0a,b,c当高度为1a,b,c当高度为2a,b,c当高度为...... 右单旋(parent->_bf -2 && cur->_bf -1)a,b,c当高度为0a,b,c当高度为1a,b,c当高…

【群智能算法】基于浣熊优化算法的工程应用问题优化【Matlab代码#43】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】1. 原始COA算法1.1 开发阶段1.2 探索阶段 2. 工程应用问题优化2.1 压力容器设计2.2 拉压弹簧设计 3. 部分代码展示4. 仿真结果展示5. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章…

Spring事件机制让你的代码更优雅

今天为大家分享一下Spring的事件机制的使用&#xff0c;它是spring中一个非常好用也很实用的机制。 1. spring事件机制的概念 Spring的事件机制是基于观察者模式实现的&#xff0c;它可以在我们的实际应用程序中实现代码之间的解耦&#xff0c;提高代码的可维护性和可扩展性。…

ShardingSphere-Proxy 分库分表

安装ShardingSphere-Proxy 中间件封装 定位为透明化的数据库代理端&#xff0c;提供封装了数据库二进制协议的服务端版本&#xff0c;用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL版本&#xff0c;它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端&#x…

面试必问:四种经典限流算法

今天给大家分享一下限流方面的&#xff0c;大家都知道&#xff0c;在分布式系统中&#xff0c;高并发场景下&#xff0c;为了防止系统因突然的流量激增而导致的崩溃&#xff0c;同时保证服务的高可用性和稳定性&#xff0c;限流是最常用的手段。希望能够给大家带来帮助&#xf…

STM32常见面试题

一、STM32F1和F4的区别&#xff1f; 内核不同&#xff1a;F1是Cortex-M3内核&#xff0c;F4是Cortex-M4内核&#xff1b; 主频不同&#xff1a;F1主频72MHz&#xff0c;F4主频168MHz&#xff1b; 浮点运算&#xff1a;F1无浮点运算单位&#xff0c;F4有&#xff1b; 功能性能&…

【无标题】vue中表单绑定v-model

表单绑定v-model 表单控件在实际开发中是非常常见的。特别是对于用户信息的提交&#xff0c;需要大量的表单。 Vue中使用v-model指令来实现表单元素和数据的双向绑定。 案例的解析&#xff1a; 当我们在输入框输入内容时 因为input中的v-model绑定了message&#xff0c;所以会…

Vue-搭建Vuex开发环境

1 安装Vuex 安装之前需要了解一个版本问题&#xff0c;在vue2中&#xff0c;要用vuex的3版本&#xff0c;在vue3中&#xff0c;要用vuex的4版本&#xff0c;要严格遵循这个版本要求&#xff0c;不然就会出现各种意想不到的问题&#xff0c;例如下方安装报错&#xff0c;就算因…

ubuntu修改应用图表|任务栏收藏|快捷方式|收藏夹

需要知道应用程序对应的.desktop文件的位置&#xff0c;然后使用sudo gedit打开。修改对应位置的信息就可以了。 参考&#xff1a;Linux下Desktop文件入门 1.desktop文件位置 一般存放在/usr/share/applications这个位置里面。 以vscode为例&#xff0c;使用sudo gedit code…

POJ - 2287 Tian Ji -- The Horse Racing

题目来源 2287 -- Tian Ji -- The Horse Racing (poj.org) 题目描述 田忌赛马是中国历史上一个著名的故事。 这个故事发生在2300年前&#xff0c;田忌是齐国的一个大官&#xff0c;他喜欢和齐王以及其他公子赛马。 田忌和齐王都有三类马&#xff0c;分别是下等马&#xff0…

1750_使用gcc对嵌入式代码控制逻辑进行测试

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 相信很多人的C语言学习是从printf开始的&#xff0c;为了验证我们的程序代码运行结果&#xff0c;我们通常会选择使用printf打印出我们计算的结果看一下是否与预期一致。到了嵌入式软件开发&#…

web前端工程师个人简历编写(附详细代码)

web前端工程师 h5css3完成简历编写&#xff0c;效果如下&#xff1a; 底部附有详细代码编写 编写Web前端工程师个人简历时&#xff0c;需要注意以下几点&#xff1a; 简洁明了&#xff1a;简历应该简洁明了&#xff0c;内容要点突出&#xff0c;避免冗长和废话。用简洁的语言…

Boost的介绍、安装与环境配置

文章目录 一、Boost库简介二、Boost的安装与编译&#xff08;一&#xff09;下载解压&#xff08;二&#xff09;编译静态库 三、配置VS环境四、其它环境的配置&#xff08;vscode、DevC&#xff09;&#xff08;一&#xff09;在DEVC中配置使用boost库的环境&#xff08;二&am…

java: 程序包javax.servlet.http不存在

问题描述 当项目从2.7.x的springboot升级到3.0.x的时候&#xff0c;遇到一个问题“java: 程序包javax.servlet.http不存在” 。这可能是一些包的精简变化导致的。错误信息如下&#xff1a; 错误代码段 package com.softdev.system.generator.config;import com.softdev.system…

深度学习-ubuntu18.04+RTX3080+cuda11.2+cudnn8.1.0下安装polarstream全纪录

&#xff11;、安装 创建一个python3.7的虚拟环境 conda create --name polarstream python3.7 激活虚拟环境 source activate polarstream以下操作均在虚拟环境中进行 安装与cuda和python版本对应的torch版本,参考https://blog.csdn.net/didadifish/article/details/12748…

【软件设计师暴击考点】操作系统知识高频考点暴击系列【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…