节省时间,提高效率:深入解析MyBatis Plus

news2024/11/27 12:38:20

1. MyBatis Plus 概述

  • Mybatis + 通用Mapper + PageHelper 升级成 MyBatis Plus

1.1 简介

官网:https://baomidou.com/

参考教程:https://baomidou.com/pages/24112f/

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

1.2 特点

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动

  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

  • 支持关键词自动转义:支持数据库关键词(order、key…)自动转义,还可自定义关键词

  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

  • 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击

    select * from user where username = 'jack' and password = '1234';
    -- 将 jack 替换成 jack' #
    -- sql注入,用户输入的内容,称为sql语句语法的一部分。
    select * from user where username = 'jack' #' and password = '1234';
    

2. 入门案例

2.1 搭建环境

  • 步骤

    • 步骤一:创建项目:test-mybatis-plus
    • 步骤二:修改pom.xml,添加依赖
    • 步骤三:创建yml文件,配置数据库相关
  • 步骤一:创建项目:test-mybatis-plus

  • 步骤二:修改pom.xml,添加依赖

        <!--确定spring boot的版本-->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.5.RELEASE</version>
        </parent>   
    
    <dependencies>
            <!-- web 开发 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--MySQL数据库驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!--支持lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--测试-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.0</version>
            </dependency>
        </dependencies>
    
  • 步骤三:创建yml文件,配置数据库相关

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/cloud_db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
        username: root
        password: 1234
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #输出日志
    

2.2 数据库和表

CREATE DATABASE cloud_db1;
USE cloud_db1;
CREATE TABLE `tmp_customer` (
  `cid` INT(11) NOT NULL AUTO_INCREMENT,
  `cname` VARCHAR(50) DEFAULT NULL,
  `password` VARCHAR(32) DEFAULT NULL,
  `telephone` VARCHAR(11) DEFAULT NULL,
  `money` DOUBLE DEFAULT NULL,
  `version` INT(11) DEFAULT NULL,
  `create_time` DATE DEFAULT NULL,
  `update_time` DATE DEFAULT NULL,
  PRIMARY KEY (`cid`)
);

INSERT  INTO `tmp_customer`(`cid`,`cname`,`password`,`telephone`,`money`,`version`,`create_time`,`update_time`) 
VALUES (1,'jack','1234','110',1000,NULL,NULL,NULL),(2,'rose','1234','112',1000,NULL,NULL,NULL),(3,'tom','1234','119',1000,NULL,NULL,NULL);

2.3 入门:查询所有

  • 步骤

    • 步骤1:配置JavaBean,添加MyBatisPlus对应的注解(表、主键、字段等)
    • 步骤2:编写dao接口,并继承BaseMapper接口
    • 步骤3:编写启动类
    • 步骤4:编写测试类
  • 步骤1:配置JavaBean

    • @TableName 表名注解,value属性设置表名
package com.czxy.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.List;

/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Data
@TableName("tmp_customer")
public class Customer {
    @TableId(type = IdType.AUTO)
    private Integer cid;
    private String cname;
    private String password;
    private String telephone;
    private String money;

    private Integer version;

    @TableField(exist = false)
    private List<Integer> ids;
}

  • 步骤2:编写dao
package com.czxy.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.mp.domain.Customer;
import org.apache.ibatis.annotations.Mapper;

/**
 * Created by liangtong.
 */
@Mapper
public interface CustomerMapper extends BaseMapper<Customer> {
}
  • 步骤3:编写启动类
package com.czxy.mp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Created by liangtong.
 */
@SpringBootApplication
public class MybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
}

  • 步骤4:编写测试类
package com.czxy;

import com.czxy.mp.MybatisPlusApplication;
import com.czxy.mp.domain.Customer;
import com.czxy.mp.mapper.CustomerMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * Created by liangtong.
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MybatisPlusApplication.class)
public class TestDemo01 {
    @Resource
    private CustomerMapper customerMapper;

    @Test
    public void testFindAll() {
        List<Customer> list = customerMapper.selectList(null);
        list.forEach(System.out::println);
    }
}

3. 基本操作

3.1 常见API

BaseMapper 封装CRUD操作,泛型 T 为任意实体对象

  • 增删改
方法名描述
int insert(T entity)插入一条记录,entity 为 实体对象
int delete(Wrapper wrapper)根据 entity 条件,删除记录,wrapper 可以为 null
int deleteBatchIds(Collection idList)根据ID 批量删除
int deleteById(Serializable id)根据 ID 删除
int deleteByMap(Map<String, Object> map)根据 columnMap 条件,删除记录
int update(T entity, Wrapper updateWrapper)根据 whereEntity 条件,更新记录
int updateById(T entity);根据 ID 修改
  • 查询
方法名描述
T selectById(Serializable id)根据 ID 查询
T selectOne(Wrapper queryWrapper)根据 entity 条件,查询一条记录
List selectBatchIds(Collection idList)根据ID 批量查询
List selectList(Wrapper queryWrapper)根据 entity 条件,查询全部记录
List selectByMap(Map<String, Object> columnMap)根据 columnMap 条件
List<Map<String, Object>> selectMaps(Wrapper queryWrapper)根据 Wrapper 条件,查询全部记录
List selectObjs( Wrapper queryWrapper)根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
IPage selectPage(IPage page, Wrapper queryWrapper)根据 entity 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage page, Wrapper queryWrapper)根据 Wrapper 条件,查询全部记录(并翻页)
Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper)根据 Wrapper 条件,查询总记录数

3.2 添加

    @Test
    public void testInsert() {
        Customer customer = new Customer();
        customer.setCname("测试");

        customerMapper.insert(customer);
    }
  • 获得自动增长列信息

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    @Test
    public void testInsert() {
        Customer customer = new Customer();
        customer.setCname("测试");
        customerMapper.insert(customer);
        System.out.println(customer);
    }

3.3 更新

  • 通过id更新
    @Test
    public void testUpdate() {
        Customer customer = new Customer();
        customer.setCid(15);
        customer.setCname("测试777");
        customer.setPassword("777");

        // 需要给Customer设置@TableId
        customerMapper.updateById(customer);
    }
  • 更新所有
	@Test
    public void testUpdate2() {
        Customer customer = new Customer();
        customer.setCname("测试777");
        customer.setPassword("777");
        // 更新所有
        customerMapper.update(customer,null);
    }

3.4 删除

  • 根据id进行删除
@Test
    public void testDelete() {
        // 需要给Customer设置@TableId
        int i = customerMapper.deleteById(11);
        System.out.println(i);
    }
  • 批量删除
    @Test
    public void testBatchDelete() {
        // 需要给Customer设置@TableId
        int i = customerMapper.deleteBatchIds(Arrays.asList(9,10));
        System.out.println(i);
    }

4 查询

4.1 Map条件

    @Test
    public void testMap(){
        Map map = new HashMap();
        map.put("cname","测试");
        map.put("password","123456");

        List list = customerMapper.selectByMap(map);
        list.forEach(System.out::println);
    }

4.2 Wrapper条件

4.2.1 wrapper介绍

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • Wrapper : 条件构造抽象类,最顶端父类

    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : Entity 对象封装操作类,不是用lambda语法
      • UpdateWrapper : Update 条件封装,用于Entity对象更新操作
      • AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
        • LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper
  • 如果想进行复杂条件查询,那么需要使用条件构造器 Wapper,涉及到如下方法

方法名描述
selectOne根据条件查询一个,结果:0或1,如果查询多个异常
selectCount查询总条数
selectList查询所有
selectMaps将一条记录封装到Map中,最后返回List<Map<Sting,Object>>
selectObjs将一条记录封装到Object中,最后返回List<Object>
update更新指定条件
delete删除指定条件
  • 拼凑条件相关关键字
查询方式说明
setSqlSelect设置 SELECT 查询字段
whereWHERE 语句,拼接 + WHERE 条件
andAND 语句,拼接 + AND 字段=值
andNewAND 语句,拼接 + AND (字段=值)
orOR 语句,拼接 + OR 字段=值
orNewOR 语句,拼接 + OR (字段=值)
eq等于=
allEq基于 map 内容等于=
ne不等于<>
gt大于>
ge大于等于>=
lt小于<
le小于等于<=
like模糊查询 LIKE
notLike模糊查询 NOT LIKE
inIN 查询
notInNOT IN 查询
isNullNULL 值查询
isNotNullIS NOT NULL
groupBy分组 GROUP BY
havingHAVING 关键词
orderBy排序 ORDER BY
orderAscASC 排序 ORDER BY
orderDescDESC 排序 ORDER BY
existsEXISTS 条件语句
notExistsNOT EXISTS 条件语句
betweenBETWEEN 条件语句
notBetweenNOT BETWEEN 条件语句
addFilter自由拼接 SQL
last拼接在最后,例如:last(“LIMIT 1”)

4.2.2 条件查询

  • 基本多条件查询
	@Test
    public void testWrapper(){
        // 拼凑条件
        QueryWrapper<Customer> queryWrapper = new QueryWrapper();
        // 1)模糊查询
        queryWrapper.like("cname","测试");
        // 2)等值查询
        queryWrapper.eq("password","777");
        // 3)批量查询
        queryWrapper.in("cid",1,2,3,4);
        // 4) 范围
        queryWrapper.ge("money", 800);
        queryWrapper.le("money" , 1500);

        // 查询
        List<Customer> list = customerMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }
  • 条件判断
    @Test
    public void findCondition2() {
        Customer customer = new Customer();
        customer.setPassword("777");
        customer.setCname("888");
        customer.setIdList(Arrays.asList(2,3,4));
        customer.setCid(3);
        //条件查询
        QueryWrapper<Customer> queryWrapper = new QueryWrapper<>();
        // 1) 等值查询
        queryWrapper.eq( customer.getPassword()!=null ,"password", customer.getPassword());
        // 2) 模糊查询
        queryWrapper.like(customer.getCname() != null , "cname",customer.getCname());
        // 3) in语句
        queryWrapper.in(customer.getIdList() != null , "cid",customer.getIdList());
        // 4) 大于等于
        queryWrapper.ge(customer.getCid() != null , "cid" , customer.getCid());


        //查询
        List<Customer> list = customerMapper.selectList(queryWrapper);
        //list.forEach(customer-> System.out.println(customer));
        list.forEach(System.out::println);

    }

4.3.3 条件更新

  • 基本更新
 	@Test
    public void testWrapperUpdate(){
        //1 更新数据
        Customer customer = new Customer();
        customer.setVersion(1);

        //2 更新条件
        UpdateWrapper<Customer> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("cid", 1,2,3);

        //3 更新
        int update = customerMapper.update(customer, updateWrapper);
        System.out.println(update);
    }

4.3 分页

4.3.1 内置插件

  • 主体插件: MybatisPlusInterceptor,该插件内部插件集:
    • 分页插件: PaginationInnerInterceptor
    • 多租户插件: TenantLineInnerInterceptor
    • 动态表名插件: DynamicTableNameInnerInterceptor
    • 乐观锁插件: OptimisticLockerInnerInterceptor
    • sql性能规范插件: IllegalSQLInnerInterceptor
    • 防止全表更新与删除插件: BlockAttackInnerInterceptor

4.3.2 配置类

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package com.czxy.mp.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 配置插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){

        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        return mybatisPlusInterceptor;
    }

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     * @return
     */
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

4.3.3 分页

@Test
    public void testPage(){
        // 分页数据
        int pageNum = 1;
        int pageSize = 3;
        Page<Customer> page = new Page<>(pageNum , pageSize);
        page.setSearchCount(true);

        // 查询
        customerMapper.selectPage(page, null);

        // 分页数据
        System.err.println("当前页码:" + page.getCurrent());
        System.err.println("每页显示记录数:" + page.getSize());
        System.err.println("总页数:" + page.getPages());
        System.err.println("总记录数:" + page.getTotal());
        System.err.println("是否有下一页:" + page.hasNext());
        System.err.println("是否有上一页:" + page.hasPrevious());
        // 分页数据列表
        page.getRecords().forEach(System.err::println);
    }

5. 常见注解

5.1 表名注解:@TableName

属性描述
value表名
keepGlobalPrefix是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.2 主键注解:@TableId

属性描述
value主键字段名
type主键类型
IdType.ASSIGN_UUID ,分配UUID,MyBatisPlus维护String数据
IdType.ASSIGN_ID ,分配ID(默认使用雪花算法)MyBatisPlus维护Long数据
IdType.AUTO ,自动增长(数据库维护)
  • 测试表:User 和 Book

    CREATE TABLE tmp_user(
        uid VARCHAR(100),
        uname VARCHAR(50)
    );
    
    CREATE TABLE tmp_book(
        bid long,
        title VARCHAR(50)
    );
    
  • JavaBean

    • User

      package com.czxy.mp.domain;
      
      import com.baomidou.mybatisplus.annotation.IdType;
      import com.baomidou.mybatisplus.annotation.TableId;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      /**
       * @author 桐叔
       * @email liangtong@itcast.cn
       */
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
          //@TableId(type = IdType.ASSIGN_UUID)		//随机一个字符串
          @TableId(type = IdType.ASSIGN_ID)			//随机一个数字(Long)
          private String uid;
          private String uname;
      }
      
      
    • Book

      package com.czxy.domain;
      
      import com.baomidou.mybatisplus.annotation.IdType;
      import com.baomidou.mybatisplus.annotation.TableId;
      import com.baomidou.mybatisplus.annotation.TableName;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      /**
       * @author 桐叔
       * @email liangtong@itcast.cn
       */
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      @TableName(value="book",keepGlobalPrefix = true)  //表名
      public class Book {
          @TableId(value = "bid", type = IdType.ASSIGN_ID)
          private Long bid;
          private String title;
      }
      
      
      
    • 测试:

      • User的UUID

        package com.czxy;
        
        import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
        import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
        import com.czxy.domain.Customer;
        import com.czxy.domain.User;
        import com.czxy.mapper.CustomerMapper;
        import com.czxy.mapper.UserMapper;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.boot.test.context.SpringBootTest;
        import org.springframework.test.context.junit4.SpringRunner;
        
        import javax.annotation.Resource;
        
        /**
         * @author 桐叔
         * @email liangtong@itcast.cn
         */
        @RunWith(SpringRunner.class)                                //spring 整合 junit测试
        @SpringBootTest(classes = TestMybatisPlusApplication.class) //spring boot整合junit测试
        public class TestCustomerMapper_04_user {
            @Resource
            private UserMapper userMapper;
        
            @Test
            public void testUserInsert() {
                User user = new User();
                user.setUname(null);
                user.setUname("jack");
        
                int insert = userMapper.insert(user);
                System.out.println(insert);
            }
        
        
        
        
        }
        
        
      • Book的Long ID

        package com.czxy;
        
        import com.czxy.domain.Book;
        import com.czxy.domain.User;
        import com.czxy.mapper.BookMapper;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.boot.test.context.SpringBootTest;
        import org.springframework.test.context.junit4.SpringRunner;
        
        import javax.annotation.Resource;
        
        /**
         * @author 桐叔
         * @email liangtong@itcast.cn
         */
        @RunWith(SpringRunner.class)                                //spring 整合 junit测试
        @SpringBootTest(classes = TestMybatisPlusApplication.class) //spring boot整合junit测试
        public class TestCustomerMapper_05_book {
            @Resource
            private BookMapper bookMapper;
        
            @Test
            public void testUserInsert() {
                Book book = new Book();
                book.setTitle("测试");
        
                int insert = bookMapper.insert(book);
                System.out.println(insert);
            }
        
        
        
        
        }
        
        

5.2 字段注解(非主键) : @TableField

属性描述
value数据库列名
fill字段自动填充策略
FieldFill.INSERT 插入时填充字段
FieldFill.UPDATE 更新时填充字段
FieldFill.INSERT_UPDATE 插入和更新时填充字段
exist是否存储到数据库(是否是临时数据)

6. 常见配置

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #输出日志
    map-underscore-to-camel-case: true  #驼峰命名
  global-config:
    db-config:
      id-type: auto  #全局配置,id自动增强
      table-prefix: tmp_ #表名前缀
  type-aliases-package: com.czxy.mp.domain #别名包扫描路径
  mapper-locations: classpath*:/mapper/**/*.xml #映射文件位置
  • 整合xml

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.czxy.mp.mapper.CustomerMapper">
    
        <select id="findAll" resultType="customer">
            select * from tmp_customer
        </select>
    
    </mapper>
    
  • CustomerMapper对应的方法

    package com.czxy.mp.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.czxy.mp.domain.Customer;
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    /**
     * @author 桐叔
     * @email liangtong@itcast.cn
     */
    @Mapper
    public interface CustomerMapper extends BaseMapper<Customer> {
    
        public List<Customer> findAll();
    }
    
    

7. 高级(插件机制)

7.1 自动填充

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。

我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

7.1.1 原理

  • 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler,确定填充具体操作
  • 注解填充字段:@TableField(fill = ...) 确定字段填充的时机
    • FieldFill.INSERT:插入填充字段
    • FieldFill.UPDATE:更新填充字段
    • FieldFill.INSERT_UPDATE:插入和更新填充字段

7.1.2 基本操作

  • 步骤一:修改表添加字段
alter table tmp_customer add column create_time date;
alter table tmp_customer add column update_time date;
  • 步骤二:修改JavaBean
package com.czxy.mp.domain;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.util.Date;

/**
 * Created by liangtong.
 */
@Data
@TableName("tmp_customer")
public class Customer {
    @TableId(type = IdType.AUTO)
    private Integer cid;
    private String cname;
    private String password;
    private String telephone;
    private String money;

    @TableField(value="create_time",fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(value="update_time",fill = FieldFill.UPDATE)
    private Date updateTime;

}

  • 步骤三:编写处理类

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package com.czxy.mp.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 插入填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
    }

    /**
     * 更新填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

  • 步骤四:测试
    @Test
    public void testInsert() {
        Customer customer = new Customer();
        customer.setCname("测试888");

        customerMapper.insert(customer);
    }

    @Test
    public void testUpdate() {
        Customer customer = new Customer();
        customer.setCid(11);
        customer.setTelephone("999");

        customerMapper.updateById(customer);
    }

7.2 乐观锁

  • 基于数据库的锁,处理的并发问题(两个操作对应一个数据)

    • 乐观锁:数据不一致,不会发生。读锁。

    • 悲观锁:数据不一致,肯定发生。写锁,又称为排它锁。

7.2.1 什么是乐观锁

  • 目的:数据必须同步。当要更新一条记录的时候,希望这条记录没有被别人更新

  • 乐观锁实现方式:(万一发生了数据不一致的解决方案)

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

7.2.2. 实现

  • 步骤:

    • 步骤1:环境准备(表version字段、JavaBean versoin属性、必须提供默认值)
    • 步骤2:使用乐观锁版本控制 @Version
    • 步骤3:开启乐观锁插件配置
  • 步骤一:修改表结构,添加version字段

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 步骤二:修改JavaBean,添加version属性

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    package com.czxy.mp.domain;
    
    import com.baomidou.mybatisplus.annotation.*;
    import lombok.Data;
    
    /**
     * Created by liangtong.
     */
    @Data
    @TableName("tmp_customer")
    public class Customer {
        @TableId(type = IdType.AUTO)
        private Integer cid;
        private String cname;
        private String password;
        private String telephone;
        private String money;
        @Version
        @TableField(fill = FieldFill.INSERT)
        private Integer version;
    }
    
    
  • 步骤三:元对象处理器接口添加version的insert默认值 (保证version有数据)

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package com.czxy.mp.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * Created by liangtong.
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 插入填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("version", 1, metaObject);
    }

    /**
     * 更新填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

  • 步骤四:修改 MybatisPlusConfig 开启乐观锁
package com.czxy.mp.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by liangtong.
 */
@Configuration
public class MybatisPlusConfig {
     */
    /**
     * 配置插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){

        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return mybatisPlusInterceptor;
    }
}

  • 步骤五:测试
    • 先添加一条,保证version有数据
    • 在更新该条
    @Test
    public void testUpdate() {
        Customer customer = new Customer();
        customer.setCid(14);
        customer.setCname("测试999");
        // 与数据库中数据一致,将更新成功,否则返回失败。
        customer.setVersion(1);

        int i = customerMapper.updateById(customer);
        System.out.println(i);
    }

7.2.3 注意事项

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • update(entity, wrapper) 方法下, wrapper 不能复用!!!
  • 数据库表的version字段,必须有默认值(SQL语句默认值、或MyBatisPlus自动填充)
  • 在进行更新操作时,必须设置version值,否则无效。

7.3 逻辑删除

7.3.1 什么是逻辑删除

  • 逻辑删除,也称为假删除。就是在表中提供一个字段用于记录是否删除,实际该数据没有被删除。

7.3.2 实现

  • 步骤:

    • 步骤一:环境(表提供字段deleted、JavaBean属性 deleted、填充默认值0)
    • 步骤二:修改JavaBean,添加注解 @TableLogic
  • 步骤一:修改表结构添加deleted字段

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 步骤二:修改JavaBean,给deleted字段添加@TableLogic

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    package com.czxy.mp.domain;
    
    import com.baomidou.mybatisplus.annotation.*;
    import lombok.Data;
    
    /**
     * Created by liangtong.
     */
    @Data
    @TableName("tmp_customer")
    public class Customer {
        @TableId(type = IdType.AUTO)
        private Integer cid;
        private String cname;
        private String password;
        private String telephone;
        private String money;
        @Version
        @TableField(fill = FieldFill.INSERT)
        private Integer version;
    
        @TableLogic
        @TableField(fill = FieldFill.INSERT)
        private Integer deleted;
    }
    
    
  • 步骤三:添加数据时,设置默认“逻辑未删除值”

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    package com.czxy.mp.config;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * Created by liangtong.
     */
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        /**
         * 插入填充
         * @param metaObject
         */
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("version", 1, metaObject);
            this.setFieldValByName("deleted", 0, metaObject);
        }
    
        /**
         * 更新填充
         * @param metaObject
         */
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }
    
    
  • 步骤四:测试

        @Test
        public void testDelete() {
            // 删除时,必须保证deleted数据为“逻辑未删除值”
            int i = customerMapper.deleteById(12);
            System.out.println(i);
        }
    

7.3.3 注意

  • 如果使用逻辑删除,将delete语句,修改成了update,条件 where deleted=0
  • 同时,查询语句自动追加一个查询条件 WHERE deleted=0。如果查询没有数据,检查deleted字段的值。

7.3.4 全局配置

  • 如果使用了全局配置,可以不使用注解@TableLogic
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 局逻辑删除的实体字段名
      logic-delete-value: 1  # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

7.3.5 恢复

  • 问题:进行逻辑删除后的数据,如何恢复(recovery)?

    • 需要自己编写SQL语句
  • 方案1:使用sql具有,更新deleted=0即可

    UPDATE `tmp_customer` SET `deleted`='0' WHERE `cid`='13';
    
  • 方案2:直接使用update语句,不能解决问题。

        @Test
        public void testUpdate() {
            Customer customer = new Customer();
            customer.setCid(13);
            customer.setDeleted(0);
            //更新
            customerMapper.updateById(customer);
        }
    
  • 方案3:修改Mapper,添加 recoveryById 方法,进行数据恢复。

    @Mapper
    public interface CustomerMapper extends BaseMapper<Customer> {
    
        @Update("update tmp_customer set deleted = 0 where cid = #{cid}")
        public void recoveryById(@Param("cid") Integer cid);
    }
    
    

8. 通用Service

8.1 分析

  • 通用service封装了service层常见的CURD方法

  • 通用Service分析

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

8.2 基本使用

  • 标准service:接口 + 实现

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • service接口

        package com.czxy.service;
        
        import com.baomidou.mybatisplus.extension.service.IService;
        import com.czxy.domain.Customer;
        
        /**
         * @author 桐叔
         * @email liangtong@itcast.cn
         */
        public interface CustomerService extends IService<Customer> {
        }
        
      
    • service实现类

      package com.czxy.service.impl;
      
      import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
      import com.czxy.domain.Customer;
      import com.czxy.mapper.CustomerMapper;
      import com.czxy.service.CustomerService;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      
      /**
       * @author 桐叔
       * @email liangtong@itcast.cn
       */
      @Service
      @Transactional
      public class CustomerServiceImpl extends ServiceImpl<CustomerMapper,Customer> implements CustomerService {
      
      }
      
      

8.3 常见方法

  • 查询所有
  • 添加
  • 修改
  • 删除
package com.czxy.test;

import com.czxy.mp.Day62MybatisPlusApplication;
import com.czxy.mp.domain.Customer;
import com.czxy.mp.service.CustomerService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author 桐叔
 * @email liangtong@itcast.cn
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Day62MybatisPlusApplication.class)
public class TestDay62CustomerService {

    @Resource
    private CustomerService customerService;

    @Test
    public void testSelectList() {
        List<Customer> list = customerService.list();
        list.forEach(System.out::println);
    }

    @Test
    public void testInsert() {
        Customer customer = new Customer();
        customer.setCname("张三");
        customer.setPassword("9999");
        // 添加
        customerService.save(customer);
    }

    @Test
    public void testUpdate() {
        Customer customer = new Customer();
        customer.setCid(14);
        customer.setCname("777");
        customer.setPassword("777");

        customerService.updateById(customer);
    }

    @Test
    public void testSaveOrUpdate() {
        Customer customer = new Customer();
        customer.setCid(15);
        customer.setCname("999");
        customer.setPassword("99");

        customerService.saveOrUpdate(customer);
    }

    @Test
    public void testDelete() {
        customerService.removeById(15);
    }
}

9 新功能

9.1 执行SQL分析打印

该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长

  • p6spy 依赖引入

    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>3.9.1</version>
    </dependency>
    
  • 核心yml配置

    spring:
      datasource:
      	# p6spy 提供的驱动代理类,
        driver-class-name: com.p6spy.engine.spy.P6SpyDriver
        # url 固定前缀为 jdbc:p6spy,跟着冒号为对应数据库连接地址
        url: jdbc:p6spy:mysql://127.0.0.1:3306...
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • spy.properties 配置

    #3.2.1以上使用
    modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    #3.2.1以下使用或者不配置
    #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
    # 自定义日志打印
    logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
    #日志输出到控制台
    appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
    # 使用日志系统记录 sql
    #appender=com.p6spy.engine.spy.appender.Slf4JLogger
    # 设置 p6spy driver 代理
    deregisterdrivers=true
    # 取消JDBC URL前缀
    useprefix=true
    # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
    excludecategories=info,debug,result,commit,resultset
    # 日期格式
    dateformat=yyyy-MM-dd HH:mm:ss
    # 实际驱动可多个
    #driverlist=org.h2.Driver
    # 是否开启慢SQL记录
    outagedetection=true
    # 慢SQL记录标准 2 秒
    outagedetectioninterval=2
    
    

9.2 数据安全保护

为了保护数据库配置及数据安全,在一定的程度上控制开发人员流动导致敏感信息泄露

  • 步骤:

    • 步骤1:使用 AES 工具类,生成秘钥
    • 步骤2:使用 AES工具类,根据步骤1生成的秘钥对敏感信息进行加密
    • 步骤3:设置加密后的配置信息
    • 步骤4:启动服务时,使用秘钥
  • 步骤1-2:使用工具类生成秘钥以及对敏感信息进行加密

    package com.czxy;
    
    import com.baomidou.mybatisplus.core.toolkit.AES;
    import org.junit.Test;
    
    /**
     * @author 桐叔
     * @email liangtong@itcast.cn
     */
    public class TestAES {
        @Test
        public void testAes() {
            String randomKey = AES.generateRandomKey();
    
            String url =  "jdbc:p6spy:mysql://127.0.0.1:3306/zx_edu_teacher?useUnicode=true&characterEncoding=utf8";
            String username = "root";
            String password = "1234";
    
            String urlAES = AES.encrypt(url, randomKey);
            String usernameAES = AES.encrypt(username, randomKey);
            String passwordAES = AES.encrypt(password, randomKey);
    
            System.out.println("--mpw.key=" + randomKey);
            System.out.println("mpw:" + urlAES);
            System.out.println("mpw:" + usernameAES);
            System.out.println("mpw:" + passwordAES);
        }
    }
    // Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
    //--mpw.key=fddd2b7a67460e16
    //mpw:7kSEISvq3QWfnSh6vQZc2xgE+XF/sJ0WS/sgGkYpCOTQRjO1poLi3gfmGZNOwKzfqZUec0odiwAdmxcS7lfueENGIx8OmIe//d9imrGFpnkrf8jNSHdzfNPCUi3MbmUb
    //mpw:qGbCMksqA90jjiGXXRr7lA==
    //mpw:xKG9GABlywqar6CGPOSJKQ==
    
  • 步骤3:配置加密信息

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 步骤4:使用秘钥启动服务

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

end

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

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

相关文章

记录 | 使用samba将ubuntu文件夹映射到windows实现共享文件夹

一、ubuntu配置 1. 安装 samba samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件&#xff0c;由服务器及客户端程序构成。SMB&#xff08;Server Messages Block&#xff0c;信息服务块&#xff09;是一种在局域网上共享文件和打印机的一种通信协议。 sudo apt-…

解决 Xshell 无法使用 root 账户远程登录 Linux 的问题

文章目录 问题描述问题原因解决办法 笔者出问题时的运行环境&#xff1a; Red Hat Enterprise Linux 9.2 x86_64 Xshell 7 问题描述 笔者在新安装的 Red Hat Enterprise Linux 中发现一个问题。在 RHEL 安装完之后&#xff0c;无法在 Xshell 中使用 root 账户远程登录此 Lin…

光学式雨量监测站-雨量监测的得力助手

随着科技的发展&#xff0c;人们对天气的预测和监测越来越依赖现代化的设备。光学式雨量监测站作为一种雨量监测仪器&#xff0c;能够实现对降雨量的测量&#xff0c;为天气预报和气候研究提供数据支持。 一、光学式雨量监测站的工作原理 WX-YJ3 光学式雨量监测站主要利用光学…

软件测试卷王的自述,我难道真的很卷?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff…

探索AIGC未来:CPU源码优化、多GPU编程与中国算力瓶颈与发展

★人工智能&#xff1b;大数据技术;AIGC;Turbo;DALLE 3;多模态大模型&#xff1b;MLLM&#xff1b;LLM&#xff1b;Agent&#xff1b;Llama2&#xff1b;国产GPU芯片&#xff1b;GPU;CPU&#xff1b;高性能计算机&#xff1b;边缘计算&#xff1b;大模型显存占用&#xff1b;5G…

今日问题:解决最新Chrome和chromedriver版本对不上的问题

from selenium import webdriver #from .chrome.webdriver import WebDriver as Chrome from selenium.webdriver.common.by import By from time import sleep driver webdriver.Chrome()driver.get("https://www.baidu.com/") driver.maximize_window()#窗口最大化…

英文论文查重复率网址

大家好&#xff0c;今天来聊聊英文论文查重复率网址&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 英文论文查重复率网址 在撰写英文论文时&#xff0c;查重是确保论文原创性和质量的重要环节快码论文…

01.项目简介

开源数字货币交易所&#xff0c;基于Java开发的货币交易所 | BTC交易所 | ETH交易所 | 数字货币交易所 | 交易平台 | 撮合交易引擎。本项目基于SpringCloudAlibaba微服务开发&#xff0c;可用来搭建和二次开发数字货币交易所。 项目特色&#xff1a; 基于内存撮合引擎&#xf…

快速实现入门HarmonyOS开发

本文档适用于HarmonyOS应用开发的初学者。编写两个简单的页面&#xff0c;实现在第一个页面点击按钮跳转到第二个页面。开始前&#xff0c;请参考下载与安装软件、配置开发环境和运行HelloWorld&#xff0c;完成开发工具的安装和开发环境的配置。 开发Ability 概述&#xff1…

ZStack Cloud构建青州市中医院核心业务云平台

青州市中医院通过ZStack Cloud云平台构建云基础设施&#xff0c;实现对原有物理机和分布式存储平台的利旧和纳管&#xff0c;有效将HIS&#xff08;医院管理系统&#xff09;、PACS&#xff08;影像系统&#xff09;等核心业务系统进行统一管理&#xff1b;同时&#xff0c;借助…

批量AI人工智能写作软件下载【2024最新】

在当今数字化的浪潮中&#xff0c;人工智能技术为各行各业带来了颠覆性的变革。其中&#xff0c;AI在文案创作领域的应用尤为引人瞩目&#xff0c;而批量AI人工智能写作更是成为文案创作者们关注的热点。本文将深入探讨批量AI人工智能写作的概念、其在不同领域的应用&#xff0…

硬件开发笔记(十四):RK3568底板电路LVDS模块、MIPI模块电路分析、LVDS硬件接口、MIPI硬件接口详解

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/134634186 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

Windows故障排除 – 连接WiFi却无法上网

Windows故障排除 – 连接WiFi却无法上网 Windows Troubleshooting - Connecting WiFi but PC Cannot Browse Internet By JacksonML 有个同学买了一台崭新的D品牌游戏本&#xff0c;i7处理器&#xff0c;英伟达RTX系列独立显卡及15寸液晶显示器&#xff0c;可谓功能强大。但是…

做一个类似东郊到家预约按摩系统需要准备哪些资料?

开发预约按摩APP需要准备哪些资料&#xff1f; 一、开发前准备材料&#xff1a; 1&#xff0e;公司的营业执照&#xff1a;需要用这个公司来申请企业支付、做域名备案等。 2&#xff0e;域名、服务器&#xff1a;域名去万网注册&#xff0c;域名需要备案&#xff1b;前期服务器…

解决Idea右侧无Maven选项的问题

在创建 Spring / SpringBoot 项目的时候可能会遇到没有 Maven 选项的问题&#xff0c;如下图&#xff1a; 我们通常这样解决&#xff1a;

实时PPP提取电离层

1、使用RTMG-APP扣除卫星端和接收机端的频内偏差后的STEC和相位平滑伪距&#xff08;600历元初始化&#xff0c;因为相位平滑伪距累计电离层残差&#xff0c;一直平滑可能发散&#xff09;的STEC对比如下图&#xff1a; 2、1、使用RTMG-APP扣除卫星端和接收机端的频内偏差后的S…

042:el-table表格表头自定义高度(亲测好用)

第042个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

二叉树oj题总结

1.检查两颗树是否相同 https://leetcode.cn/problems/same-tree/ 分成子问题和结束条件 &#xff0c;这里用前序的思想解题&#xff08;先判断根&#xff0c;再左右子树&#xff09;&#xff0c;不然会很浪费时间。假如左右相等&#xff0c;最后根不同&#xff0c;白白比较了 …

Linux高级管理-搭建网站服务

在Ihternet 网络环境中&#xff0c;Web 服务无疑是最为流行的应用系统。有了Web站点&#xff0c;企业可以充分 展示自己的产品&#xff0c;宣传企业形象。Web站点还为企业提供了与客户交流、电子商务交易平台等丰富 的网络应用。部署与维护Web 服务是运维工程师必须掌握的一个技…

【抽象策略模式】实践

前言 刚果商城&#xff0c;用户登录 Or 注册 发送邮箱验证码场景&#xff0c;使用抽象策略模式实现 什么是抽象策略模式 抽象策略模式是一种行为型设计模式&#xff0c;它允许定义一系列算法&#xff0c;将每个算法封装起来&#xff0c;并使它们可以互相替换。这使得客户端代码…