xiaoye-Blog

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

官网文档:https://baomidou.com/

1.简介

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

1.1、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

2.快速开始

  1. 创建数据库mybatis_plus

  2. 创建表user

    DROP TABLE IF EXISTS user;
    
    CREATE TABLE user
    (
        id BIGINT(20) NOT NULL COMMENT '主键ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
        age INT(11) NULL DEFAULT NULL COMMENT '年龄',
        email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
        PRIMARY KEY (id)
    );
    
  3. 插入数据

    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com'),
    (2, 'Jack', 20, 'test2@baomidou.com'),
    (3, 'Tom', 28, 'test3@baomidou.com'),
    (4, 'Sandy', 21, 'test4@baomidou.com'),
    (5, 'Billie', 24, 'test5@baomidou.com');
    
  4. 创建项目并初始化!!

  5. 导入依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    

    注:尽量不要同时导入mybatis和mybatis-plus依赖,可能会产生冲突

  6. 配置数据源

    spring:
      datasource:
        username: root
        password: 18227022334a
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    
  7. 在对应的UserMaaper接口上继承BaseMapper

    @Repository
    public interface UserMapper extends BaseMapper<User> {
    }
    

    注:

    • 不需要再去配置xml文件,所有基本的查询语句是在BaseMapper接口中了!!,基本的CRUD以已经实现,但是如果有特殊的sql语句,还是需要自己编写!!
    • 需要在继承的BaseMaaper传入pojo的实体类泛型
  8. 测试

    @SpringBootTest
    class MybatisPlusStudyApplicationTests {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            List<User> userList = userMapper.selectList(null);
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

    注:UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

3.配置日志

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4.CRUD拓展

4.1、插入

  1. 在插入数据的时候,如果主键id我们没有自己设定,那么会自动生成一个全局唯一的id,并且会自动回填到我们的对象里面,也就是会自动将我们原本没有设置id的对象的id赋值!!

  2. 主键生成策略:

  3. 配置主键生成策略:

    public class User {
        @TableId(type = IdType.ASSIGN_ID)//默认
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    

    IdType:

    描述
    AUTO 数据库 ID 自增
    NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
    INPUT insert 前自行 set 主键值
    ASSIGN_ID 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
    ASSIGN_UUID 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)
    ID_WORKER 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)(弃用)
    UUID 32 位 UUID 字符串(please use ASSIGN_UUID)(弃用)
    ID_WORKER_STR 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)(弃用)

    注:使用自增的话如果不成功可能跟数据库没有勾选自增选项有关!!

4.2、更新

  1. 更新的时候虽然名字是ById,但是需要传入的是一个适配的泛型类对象,同时通过日志可以发现,mybatis-plus会根据条件自动帮我们拼接sql语句,即加入某一字段值为空,那么设置的时候就不会set该字段!!

  2. 自动填充

    • 阿里巴巴开发手册表明,所有的数据库表都应该包含的两个字段:gmt_create,gmt_modified,也就是创建时间以及修改时间,而有关这个的操作,我们都应该使用自动化来完成!!

      • 数据库级别操作(实际工作不被允许):使用默认值的方式

        image-20220823122409357

    • 代码级别:

      • 使用@TableField(fill=...)注解

        //@TableField(fill = FieldFill.DEFAULT) 默认
        //有以下值:DEFAULT, INSERT,UPDATE,INSERT_UPDATE;
        
        @TableField(fill = FieldFill.INSERT)
        private Data gmtCreate;//mybatis-plus自动开启驼峰命名
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Data gmtModified;
        
      • 编写自定义实现类 MyMetaObjectHandler处理这个注解

        @Slf4j
        @Component
        public class MyMetaObjectHandler implements MetaObjectHandler {
           
          //插入时自动填充策略
            @Override
            public void insertFill(MetaObject metaObject) {
                log.info("start insert fill......");
                this.setFieldValByName("gmtCreate",LocalDateTime.now(),metaObject);
                this.setFieldValByName("gmtModified",LocalDateTime.now(),metaObject);
            }
        
            //更新时自动填充策略
            @Override
            public void updateFill(MetaObject metaObject) {
                log.info("start update fill......");
                this.setFieldValByName("gmtModified", LocalDateTime.now(),metaObject);
            }
        }
        
      • 测试!!

      • 注:当数据类型为long时,应该在数据后面加上l表示数据为长整数!!

  3. 乐观锁悲观锁

    • 乐观锁:十分乐观,总是认为不会出现问题,无论干什么都不会上锁,如果出现问题再次进行各更新值测试!

    • 悲观锁:十分悲观,总是认为干什么都会出现问题,无论干什么都会上锁,在去操作!

    • 当要更新一条记录的时候,希望这条记录没有被别人更新,乐观锁实现方式:

      1. 取出记录时,获取当前 version
      2. 更新时,带上这个 version
      3. 执行更新时, set version = newVersion where version = oldVersion
      4. 如果 version 不对,就更新失败
      5. 说明:
        • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
        • 整数类型下 newVersion = oldVersion + 1
        • newVersion 会回写到 entity
        • 仅支持 updateById(id)update(entity, wrapper) 方法
        • update(entity, wrapper) 方法下, wrapper 不能复用!!!
    • 实现乐观锁:

      1. 数据库添加字段version

      2. 实体类添加属性

        @Version
        private Integer version;
        
      3. 新建一个配置类配置插件

        @Configuration
        @MapperScan("com.xiaoye.mapper")//可以不用放在启动类上,也可以放在这里
        @EnableTransactionManagement//自动管理事务
        public class MyBatisPlusConfig {
            //注册乐观锁插件
            @Bean
            public MybatisPlusInterceptor mybatisPlusInterceptor() {
                MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
                mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
                return mybatisPlusInterceptor;
            }
        }
        
      4. 测试!!

4.3、查询

  1. 常用查询:

    • selectById():根据id查询
    • selectList():查询全部
    • selectBatchIds():批量查询,参数是一个集合
    • selectByMap():条件查询,参数为map,map的键为字段名,值为字段值
  2. 分页查询:

    1. 在配置类下增加插件

      /**
           * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
           */
      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor2() {
          MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
          interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
          return interceptor;
      }
      
    2. 使用

      void contextLoads() {
          //第一个参数:当前页
          //第二个参数:页面个数
          Page<User> userPage = new Page<>(2,3);
          userMapper.selectPage(userPage, (Wrapper<User>) null);
          for (User record : userPage.getRecords()) {
              System.out.println(record);
          }
      }
      ///userPage.getTotal()得到数据总数
      

4.4、删除

  1. 常用删除:

    //
    userMapper.delete();
    //批量删除
    userMapper.deleteBatchIds();
    //根据id删除
    userMapper.deleteById();
    //根据条件删除
    userMapper.deleteByMap();
    
  2. 逻辑删除:在数据库中不直接删除,而是通过一个逻辑变量,比如is_delete=0|1,而不是真正的删除

    补充说明:

    • 只对自动注入的 sql 起效:
    • 插入: 不作限制
    • 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
    • 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
    • 删除: 转变为 更新
    1. 在数据库增加一个is_delete字段

    2. 在实体类中增加属性(3.3以后可以不用加注解)

      @TableLogic
      private Integer isDelete;
      
    3. 在yaml中配置

      mybatis-plus:
        global-config:
          db-config:
            logic-delete-field: isDelete # 全局逻辑删除的实体字段名
            logic-delete-value: 1 # 逻辑已删除值(默认为 1)
            logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
      
    4. 测试!!

    5. 常见问题:

      1. 如何插入:
        • 数据库定义默认值(推荐)
        • 自己set
        • 使用自动填充功能
      2. 删除接口功能自动填充功能失效:
        • 使用 deleteById 方法(推荐)
        • 使用 update 方法并: UpdateWrapper.set(column, value)(推荐)
        • 使用 update 方法并: UpdateWrapper.setSql("column=value")

5.性能分析插件

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

  1. 导入依赖:

    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>3.2.0</version>
    </dependency>
    
  2. application.yaml更改数据库方方面的配置

    username: root
    password: 18227022334a
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    
  3. 配置spy.properties

    #3.2.1以上使用
    modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    # 自定义日志打印
    logMessageFormat=com.xiaoye.log.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
    
  4. 编写自定义日志类

    @Slf4j
    public class P6SpyLogger implements MessageFormattingStrategy {
    
        private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @Override
        public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
            String date = dateFormat.format(new Date());
            if (log.isInfoEnabled()) {
                log.info("执行时间: {}", date);
                log.info("sql: {}", sql);
                log.info("耗时:{} 毫秒", elapsed);
            }
            return "";
        }
    }
    
  5. 注意:

    • driver-class-name 为 p6spy 提供的驱动类
    • url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
    • 打印出 sql 为 null,在 excludecategories 增加 commit
    • 批量操作不打印 sql,去除 excludecategories 中的 batch
    • 批量操作打印重复的问题请使用 MybatisPlusLogFactory (3.2.1 新增)
    • 该插件有性能损耗,不建议生产环境使用。

6.条件构造器Wrapper

  1. QueryWrapper:select,delete使用

  2. UpdateWrapper:update使用

  3. 常用的:

  4. 可以使用链式编程:

    public void test1(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.gt("age",21).like("name","李");
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }
    

7.代码生成器

  1. 导入依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity</artifactId>
        <version>1.7</version>
    </dependency>
    
  2. 编写自动生成代码类

    package com.xiaoye;
    
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.IFill;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.DateType;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    import com.baomidou.mybatisplus.generator.fill.Column;
    import com.baomidou.mybatisplus.generator.fill.Property;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public  class Test{
        private static final DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC", "root", "18227022334a")
                .build();
        public static void main(String[] args) {
            /**
             * 代码生成
             */
            GlobalConfig globalConfig = new GlobalConfig.Builder()
                    .author("小也")
                    .dateType(DateType.TIME_PACK)
                    .commentDate("yyyy-MM-dd hh:mm:ss")
                    .outputDir(System.getProperty("user.dir") + "/src/main/java")
                    .build();
    
            PackageConfig packageConfig = new PackageConfig.Builder()
                    .parent("com.xiaoye")
                    .entity("pojo")
                    .service("service")
                    .serviceImpl("service.Impl")
                    .mapper("mapper")
                    .xml("mapper.xml")
                    .controller("controller")
                    .build();
    
            StrategyConfig strategyConfig = new StrategyConfig.Builder()
                    .entityBuilder()
                    .enableTableFieldAnnotation()
                    .enableLombok()
                    .enableTableFieldAnnotation()
                    .versionColumnName("version")
                    .versionPropertyName("version")
                    .logicDeleteColumnName("is_delete")
                    .logicDeletePropertyName("isDelete")
                    .columnNaming(NamingStrategy.underline_to_camel)
                    .addTableFills(new Column("gmt_create", FieldFill.INSERT))
                    .addTableFills(new Column("gmt_modified", FieldFill.INSERT))
                    .addTableFills(new Property("gmtCreate", FieldFill.INSERT_UPDATE))
                    .addTableFills(new Property("gmtModified", FieldFill.INSERT_UPDATE))
                    .build();
    
            StrategyConfig strategyConfig1 = new StrategyConfig.Builder()
                    .controllerBuilder()
                    .enableHyphenStyle()
                    .enableRestStyle()
                    .build();
    
            StrategyConfig strategyConfig2 = new StrategyConfig.Builder()
                    .serviceBuilder()
                    .build();
    
            StrategyConfig strategyConfig3 = new StrategyConfig.Builder()
                    .mapperBuilder()
                    .enableMapperAnnotation()
                    .enableBaseResultMap()
                    .enableBaseColumnList()
                    .build();
            AutoGenerator generator = new AutoGenerator(dataSourceConfig);
            generator.global(globalConfig);
            generator.strategy(strategyConfig);
            generator.strategy(strategyConfig1);
            generator.strategy(strategyConfig2);
            generator.strategy(strategyConfig3);
            generator.packageInfo(packageConfig);
            generator.execute();
        }
    }
    

8.配置文件

mybatis-plus:
  mapperPackage: com.**.**.mapper
  # 对应的 XML 文件位置
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # 实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.**.**.domain
  # 针对 typeAliasesPackage,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
  #typeAliasesSuperType: Class<?>
  # 如果配置了该属性,SqlSessionFactoryBean 会把该包下面的类注册为对应的 TypeHandler
  #typeHandlersPackage: null
  # 如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
  #typeEnumsPackage: null
  # 启动时是否检查 MyBatis XML 文件的存在,默认不检查
  checkConfigLocation: false
  # 通过该属性可指定 MyBatis 的执行器,MyBatis 的执行器总共有三种:
  # SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
  # REUSE:该执行器类型会复用预处理语句(PreparedStatement)
  # BATCH:该执行器类型会批量执行所有的更新语句
  executorType: SIMPLE
  # 指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
  configurationProperties: null
  configuration:
    # 自动驼峰命名规则(camel case)映射
    # 如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
    mapUnderscoreToCamelCase: true
    # 默认枚举处理类,如果配置了该属性,枚举将统一使用指定处理器进行处理
    # org.apache.ibatis.type.EnumTypeHandler : 存储枚举的名称
    # org.apache.ibatis.type.EnumOrdinalTypeHandler : 存储枚举的索引
    # com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler : 枚举类需要实现IEnum接口或字段标记@EnumValue注解.
    defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
    # 当设置为 true 的时候,懒加载的对象可能被任何懒属性全部加载,否则,每个属性都按需加载。需要和 lazyLoadingEnabled 一起使用。
    aggressiveLazyLoading: true
    # MyBatis 自动映射策略
    # NONE:不启用自动映射
    # PARTIAL:只对非嵌套的 resultMap 进行自动映射
    # FULL:对所有的 resultMap 都进行自动映射
    autoMappingBehavior: PARTIAL
    # MyBatis 自动映射时未知列或未知属性处理策
    # NONE:不做任何处理 (默认值)
    # WARNING:以日志的形式打印相关警告信息
    # FAILING:当作映射失败处理,并抛出异常和详细信息
    autoMappingUnknownColumnBehavior: NONE
    # Mybatis一级缓存,默认为 SESSION
    # SESSION session级别缓存,同一个session相同查询语句不会再次查询数据库
    # STATEMENT 关闭一级缓存
    localCacheScope: SESSION
    # 开启Mybatis二级缓存,默认为 true
    cacheEnabled: true
  global-config:
    # 是否打印 Logo banner
    banner: true
    # 是否初始化 SqlRunner
    enableSqlRunner: false
    dbConfig:
      # 主键类型
      # AUTO 数据库ID自增
      # NONE 空
      # INPUT 用户输入ID
      # ASSIGN_ID 全局唯一ID
      # ASSIGN_UUID 全局唯一ID UUID
      idType: AUTO
      # 表名前缀
      tablePrefix: null
      # 字段 format,例: %s,(对主键无效)
      columnFormat: null
      # 表名是否使用驼峰转下划线命名,只对表名生效
      tableUnderline: true
      # 大写命名,对表名和字段名均生效
      capitalMode: false
      # 全局的entity的逻辑删除字段属性名
      logicDeleteField: deleteFlag
      # 逻辑已删除值
      logicDeleteValue: 1
      # 逻辑未删除值
      logicNotDeleteValue: 0
      # 字段验证策略之 insert,在 insert 的时候的字段验证策略
      # IGNORED 忽略判断
      # NOT_NULL 非NULL判断
      # NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
      # DEFAULT 默认的,一般只用于注解里
      # NEVER 不加入 SQL
      insertStrategy: NOT_EMPTY
      # 字段验证策略之 update,在 update 的时候的字段验证策略
      updateStrategy: NOT_NULL
      # 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
      selectStrategy: NOT_EMPTY
posted on 2022-08-30 19:56  小也取不到名字  阅读(111)  评论(0)    收藏  举报