最新 MyBatis-Plus 从入门到实战,看这一篇就够了

前言

  1. 重要的事情先开头说,这篇博客篇幅很长,我相信你耐心看完这一篇 MyBatis-Plus 之后,绝对能将它应用到实战当中,因为这篇是结合SpringBoot(快速搭建开发环境),MySQL一套讲的。
  2. 在说 MyBatis-Plus 之前,我们先回顾一下 MyBatis,MyBatis 是一个简化 JDBC 操作数据库的 ORM 框架,MyBatis 最大的特点就是省去了原生的 JDBC 操作数据库的繁琐过程,但是唯独 SQL 语句让我们自己编写,因为框架一般都是简化某某某操作,却很少能优化某某某操作。我们可以根据特定的业务编写出最优的 SQL 语句,最大力度的提高数据库查询效率,这也是 MyBatis 和 JPA(JPA是一套操作数据库的接口规范,它最让人熟悉的实现框架就是Hibernate)最大的区别之处,JPA 不管是单表还是多表操作,都不用写 SQL,这样在复杂的查询情况下效率就会很低下。
  3. 好了,说完了 MyBatis,现在开始说 MyBatis-Plus,Plus 就是增强的意思,增强什么呢?上面说了 MyBatis 最大的特点就是在省去大量繁琐的相同操作过程之外,还可以让我们可以自定义 SQL 语句。如果不是需要优化的 SQL,我相信大多数人都不想写SQL,看来 MyBatis 还是不太懂我们的心思(偷懒是每个人都想做的事情),这是号称 MyBatis 的孪生兄弟 MyBatis-Plus 就出来了,MyBatis-Plus 的宗旨是为了简化开发而生(简化 MyBatis 操作)
  4. 下面摘自 MyBatis-Plus官网阐述 MyBatis-Plus 的特性,下图也是来源于其官网。
    1. 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
    2. 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
    3. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求。
    4. 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
    5. 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
    6. 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
    7. 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
    8. 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
    9. 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
    10. 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
    11. 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
      在这里插入图片描述

MyBatis-Plus 架构

简单谈谈 MyBatis-Plus 底层实现原理

  1. 我们使用了 MyBatis-Plus 的时候,需要在 SpringBoot 的启动类上面添加 @MapperScan 注解来扫描 Mapper 接口,这样就能通过反射+动态代理生成 Mapper 接口的实现类了。
  2. 然后我们会创建一些与数据库表对应的实体类,这时 MyBatis-Plus 会通过反射机制自动扫描实体类的内部结构,然后基于抽取出来的属性信息来映射成表字段,然后再生成对应的单表增删改查 SQL 语句,最后将这些 SQL 语句注入到 MyBatis 容器当中。
  3. 通过上面的简单概述,我们可以知道 MyBatis-Plus 为我们简化了单表的增删改查,因为 MyBatis-Plus 生成单表的增删改查和我们自己写的效率是差不多的,这对于我们开发来说方便了许多。
    在这里插入图片描述

MyBatis-Plus 快速入门(结合 SpringBoot )

  1. 使用 Spring 脚手架创建 SpringBoot 项目,如果不太熟悉 IDEA 快速生成 SpringBoot 项目,可以先看下面一篇博客,几分钟就搞定。
    SpringBoot 快速入门
  2. 导入相关依赖
    下面的依赖应该是一个基本的 SpringBoot 项目必备的,每个依赖都添加注释说明
    <dependencies>
        <!-- MyBatis-Plus 依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- MySql 驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- Lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- Web 开发依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot 测试环境依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
  3. 数据库表以及插值语句
    简单说一下表字段作用(选说)
    1. create_time,update_time为了之后讲解 MyBatis-Plus 的自动填充而创建的
    2. deleted为了之后讲解 MyBatis-Plus 的逻辑删除而创建的
    3. 其他的字段都是普通的信息字段
    CREATE TABLE `user` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `name` varchar(30) DEFAULT NULL COMMENT '姓名',
      `age` int(11) DEFAULT NULL COMMENT '年龄',
      `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `update_time` datetime DEFAULT NULL COMMENT '更新时间',
      `deleted` int(1) DEFAULT NULL COMMENT '逻辑删除',
      `sex` int(1) DEFAULT '1' COMMENT '1-男,0-女',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB CHARSET=utf8;
    
    INSERT INTO `user` VALUES (1, 'zs', 18, 'zs@zs.com', '2020-07-28 21:59:24', '2020-05-13 01:17:01', NULL, 1);
    INSERT INTO `user` VALUES (2, 'ls', 20, 'ls@ls.com', '2020-07-28 21:59:21', '2020-05-13 01:17:01', NULL, 1);
    INSERT INTO `user` VALUES (3, 'ww', 19, 'ww@qq.com', '2020-07-28 19:44:38', '2020-05-13 01:17:01', NULL, 1);
    INSERT INTO `user` VALUES (4, 'zl', 21, 'zl@zl.com', '2020-07-28 21:59:27', '2020-05-13 01:17:01', NULL, 1);
    INSERT INTO `user` VALUES (5, 'sq', 24, 'sq@sq.com', '2020-07-28 21:59:29', '2020-05-13 01:17:01', NULL, 1);
    INSERT INTO `user` VALUES (6, 'xe', 21, 'xe@xe.com', '2020-05-12 09:13:35', '2020-05-13 01:26:53', NULL, 1);	
    
  4. 编写实体类
    1. 实体类上面的注解都是 Lombok 插件的注解,都是为了简化实体类而诞生的
    2. 属性上面的注解是 MyBatis-Plus 提供的注解,之后再详细说明每一个注解的作用
    @Data
    @EqualsAndHashCode(callSuper = false)
    @AllArgsConstructor
    @NoArgsConstructor
    @TableName("user")
    public class User extends Model<User> {
        @TableId(type=IdType.AUTO)
        private Long id;
        private String name;
        private Integer age;
        private String email;
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
        @TableLogic
        private Integer deleted;
        private SexEnum sex;
    }
    
  5. 配置数据源信息
    这里简单配置4大数据源参数,数据库连接池采用 SpringBoot 默认的(可以尝试自己替换成阿里的 Druid)
    spring:
      datasource:
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/mybatis-plus?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
    
  6. 扫描 Mapper 接口,在 SpringBoot 启动类中添加 @MapperScan 注解,扫描 Mapper 接口文件夹,下面是我项目中的 Mapper 接口所在的包名,大家换成自己的包名即可。
    @SpringBootApplication
    @MapperScan("cn.zwq.mpmybaitsplus.mapper")
    public class MpMybaitsplusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MpMybaitsplusApplication.class, args);
        }
    
    }
    
  7. 编写 Mapper 接口,下面接口我们继承了 MyBatis-Plus 中的 BaseMapper 接口,该接口内置了单表的增删改查方法,根据 Java 中的继承特点,子接口(我们自己编写的 UserMapper )也就拥有了单表的增删改查方法。
    public interface UserMapper extends BaseMapper<User> {}
    
    下图就是 BaseMapper 接口内置的方法,增删改查方法都涵盖了,包括条件查询的增删改查方法。
    在这里插入图片描述
  8. 在 SpringBoot 测试环境下,测试 SpringBoot 整合 MyBatis-Plus 是否成功
    1. 查询全部数据
      Wrapper 是 MyBatis-Plus 用来封装查询条件的,如果为null,说明没有查询条件
      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class MpMybaitsplusApplicationTests {
      
          @Autowired
          private UserMapper userMapper;
      
          @Test
          public void selectList(){
              /**
               *  查询全部记录
               *  @param queryWrapper 实体对象封装操作类(可以为 null)
               *  List<T> selectList (@Param(Constants.WRAPPER) Wrapper < T > queryWrapper);
               */
              List<User> users = userMapper.selectList(null);
              users.forEach(System.out::println);
          }
      
      }
      
      下图查询结果如下图所示,我们调用了方法,内部自动发送了SQL语句,根据所有字段查询记录,也做了一些SQL优化操作。
      在这里插入图片描述
      如果大家的控制台没有显示 SQL 语句,说明还没配置 MyBatis-Plus 相关日志,可以按照下图开启 MyBatis-Plus 日志。
      在这里插入图片描述
  9. 这样我们就完成了 MyBatis-Plus 入门操作了,下面将会再详细介绍 MyBatis-Plus 增删改查操作,不过下面讲解不会再像上面一样截图说明了,比如增删改代码都是直接贴代码讲流程思路,都是经过我验证了(成功更改数据库数据),所以贴出来的一般代码不会有问题,除非你们的环境与我现在开发的环境不同。

BaseMapper 方法详细说明

  1. 上面也说了,使用 MyBatis-Plus 之后,我们只需要继承 BaseMapper 即可简化单表的增删改查操作。
  2. BaseMapper 接口方法如下
    在这里插入图片描述

Insert方法

  1. 插入一条记录
    int insert(T entity);

Delete方法

  1. 根据 entity 条件,删除记录
    int delete(@Param(Constants.WRAPPER) Wrapper wrapper);
  2. 删除(根据ID 批量删除)
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
  3. 根据 ID 删除
    int deleteById(Serializable id);
  4. 根据 columnMap 条件,删除记录
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

Update方法

  1. 根据 whereEntity 条件,更新记录
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper updateWrapper);
  2. 根据 ID 修改
    int updateById(@Param(Constants.ENTITY) T entity);

Select方法

  1. 根据 ID 查询
    T selectById(Serializable id);
  2. 根据 entity 条件,查询一条记录
    T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper);
  3. 查询(根据ID 批量查询)
    List selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
  4. 根据 entity 条件,查询全部记录
    List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
  5. 查询(根据 columnMap 条件)
    List selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
  6. 根据 Wrapper 条件,查询全部记录
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper);
  7. 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
    List selectObjs(@Param(Constants.WRAPPER) Wrapper queryWrapper);
  8. 根据 entity 条件,查询全部记录(并翻页)
    IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
  9. 根据 Wrapper 条件,查询全部记录(并翻页)
    IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
  10. 根据 Wrapper 条件,查询总记录数
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper);

Wrapper条件封装器

  1. Wrapper 是 MyBatis-Plus 中提供的条件封装器,重点掌握几个 Wrapper 的实现类即可。
    下面圈起来的重点掌握即可,不过我常用的是上面两个圈起来的,下面两个是基于 Lambda 表达式的条件封装器。之后再结合案例讲解它们的具体用法。
    在这里插入图片描述

MyBatis-Plus 下的实体类详细说明

  1. 实体类对应数据库表
  2. 属性对应字段

@TableName

  1. 当实体类类名与数据库表名称不一样的时候,就需要使用这个注解
    在这里插入图片描述

@TableId

  1. 标识这个属性为数据库表主键,MyBatis-Plus提供很多种数据库主键生成策略,都定义在这个枚举类 IdType 当中。

@TableField

  1. 对象属性名与字段名不一致问题(非驼峰)

  2. 对象属性不存在表字段对应

    属性类型必须指定默认值描述
    valueString“”字段名,如果属性名和字段名一样,就不需要指定
    elstring
    existbooleantrue是否为数据库表字段,不是需要指定为false
    fillEnumFieldFill.DEFAULT字段自动填充策略,比如在插入或者更新数据的时候, 可以使用自动填充策略,这样就不需要显示赋值了。
    selectbooleantrue是否进行select查询。为false在查询时就不会查询该字段

详细讲解 MyBatis-Plus 增删改查操作

  1. 接下来使用代码讲解 MyBatis-Plus 增删改查方法,结合代码与流程思路说明,可能不截图展示结果了。

insert方法

  1. 插入对象需要我们创建一个 User 对象,然后为对象属性赋值,最后调用 MyBatis-Plus 插入方法即可。
    在这里插入图片描述
    @Test
    public void insertUser(){
        User user = new User();
        user.setAge(22);
        user.setName("xq");
        user.setEmail("xq@qq.com");
        userMapper.insert(user);
        System.out.println(user);
    }
    

主键生成策略

  1. 上面方法执行之后,成功往数据库插入一条数据,我们都知道插入数据我们需要生成主键,MyBatis-Plus 提供了很多的主键生成策略,MyBatis-Plus 默认为我们插入的数据生成主键策略是全局唯一主键:UUID
  2. 如果需要修改成主键自增,需要数据库支持主键自增功能
    @TableId(type=IdType.AUTO)
    private Long id;
    

update方法

  1. 根据 ID 修改数据
    @Test
    public void updateUser(){
        User user = new User();
        user.setName("wangwu");
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("name","ww");
        userMapper.update(user,userUpdateWrapper);
    }
    
    在这里插入图片描述
  2. 封装修改条件来修改数据
    1. 创建 User 对象,然后设置需要修改的值
    2. 根据 userUpdateWrapper 的 eq方法封装修改条件,修改名字等于 ww 的用户
    @Test
    public void updateUser(){
        User user = new User();
        user.setName("wangwu");
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("name","ww");
        userMapper.update(user,userUpdateWrapper);
    }
    
    在这里插入图片描述

delete方法

删除方法的话,下面都列出了每个方法的作用,删除就分为两种情况:一是有条件删除,而是没有条件删除

  1. 根据id删除

    /**
     *  根据id删除用户
     */
    @Test
    public void deleteUserById(){
        userMapper.deleteById(6L);
    }
    
  2. 根据id批量删除

    /**
     *  根据id批量删除用户
     */
    @Test
    public void deleteBatchById(){
        userMapper.deleteBatchIds(Arrays.asList(3L,5L));
    }
    
  3. 封装Map,每一个key都是where条件

    @Test
    public void deleteUserByMap(){
        Map<String,Object> map = new HashMap<>();
        map.put("name","zwq");
        int result = userMapper.deleteByMap(map);
        System.out.println(result);
        map.put("age",39);
        result = userMapper.deleteByMap(map);
        System.out.println(result);
    }
    
  4. 封装Wrapper,传入entity充当where条件

    /**
     *  构造条件封装器,传入的entity充当where条件
     */
    @Test
    public void deleteUserByWrapper(){
        User user = new User();
        user.setName("张三");
        user.setAge(39);
        Wrapper wrapper = new QueryWrapper(user);
        userMapper.delete(wrapper);
    }
    

select方法

查询方法的话,下面都列出了每个方法的作用,查询就分为两种情况:一是有条件查询,而是没有条件查询

  1. 根据id查找
    @Test
    public void selectUserById(){
    	System.out.println(userMapper.selectById(1L));
    }
    
  2. 根据id批量查询
    @Test
    public void selectBatchUserById(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1l, 2l, 3l, 4l, 5l, 6l));
        users.forEach(System.out::println);
    }
    
  3. 封装Wrapper条件,查询一条数据,超过一条就会报错
    /**
     *	两个eq最终会形成AND
     */
    @Test
    public void selectOne(){
        QueryWrapper<User> wrapper = new QueryWrapper();
        wrapper.eq("age",19).eq("name","ls");
        User user = userMapper.selectOne(wrapper);
        System.out.println(user);
    }
    
  4. 封装Wrapper条件,查询总数据个数
    /**
     *  selectCount
     * 		查询总记录数,参数为null,说明没有条件
     *      如果封装Wrapper条件,则会筛选查找总记录数
     */
    @Test
    public void selectCount(){
    
        Integer selectCount = userMapper.selectCount(null);
        System.out.println(selectCount);
        
        QueryWrapper<User> wrapper = new QueryWrapper();
        wrapper.eq("age",19).eq("name","zwq");
        Integer selectCount = userMapper.selectCount(wrapper);
        System.out.println(selectCount);
    }
    
  5. 封装Wrapper条件,查询总数据
    ```java
    /**
     *  selectList
     * 		查询全部数据,参数为null,说明没有条件
     *      如果封装Wrapper条件,则会筛选查找总记录数
     */
    @Test
    public void selectList(){
    
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
        
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("age",18);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    
  6. 封装Wrapper条件,分页查询数据
    分页查询在 MyBatis-Plus 当中需要配置分页插件,不配置,分页不生效
    @Configuration
    public class MyBatisPlusConfig {
    
        /**
         * 分页插件
         * @return
         */
        @Bean
        public PaginationInterceptor paginationInterceptor(){
            return new PaginationInterceptor();
        }
    }
    
    @Test
    public void selectPage(){
        QueryWrapper wrapper = new QueryWrapper();
        //年龄大于18
        wrapper.gt("age",18);
        Page<User> page = new Page<>(0,2);
        IPage<User> userIPage = userMapper.selectPage(page,wrapper);
        userIPage.getRecords().forEach(System.out::println);
    }
    

结合案例讲解 Wrapper 条件封装器

  1. 重点说明,掌握好 Wrapper 将会省去大量的编码工作,所以耐心看完每一个 Wrapper 代码案例,绝对收获不少。
  2. Wrapper 条件构造器用于增删改查的时候注入条件筛选。
    1. 使用 Wrapper 的 API 接口生成 where 条件
    2. 传入 entity 生成 where 条件

Wrapper 基本操作API

这些 API 最终都会转换为 where 筛选条件

  1. eq:等于
    @Test
    public void eq(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄为21的用户,age:说明是根据年龄筛选,年龄为21
        queryWrapper.eq(false,"age",21);
        List<User> users = userMapper.selectList(queryWrapper);
        //List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  2. ne:不等于
    @Test
    public void ne(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄不等于90的用户,age:说明是根据年龄筛选,年龄不等于90
        queryWrapper.ne("age",90);
        List<User> users = userMapper.selectList(queryWrapper);
        //List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  3. gt:大于
    @Test
    public void gt(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄大于等于90的用户,age:说明是根据年龄筛选,年龄大于等于90
        queryWrapper.gt("age",90);
        List<User> users = userMapper.selectList(queryWrapper);
        //List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  4. ge:大于等于
    @Test
    public void ge(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄大于等于90的用户,age:说明是根据年龄筛选,年龄大于等于90
        queryWrapper.ge("age",90);
        List<User> users = userMapper.selectList(queryWrapper);
        //List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  5. lt:小于
    @Test
    public void lt(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄小于90的用户,age:说明是根据年龄筛选,年龄小于90
        queryWrapper.lt("age",90);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  6. le:小于等于
    @Test
    public void le(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄小于等于90的用户,age:说明是根据年龄筛选,年龄小于等于90
        queryWrapper.le("age",90);
        List<User> users = userMapper.selectList(queryWrapper);
        //List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  7. between:在…之间
    @Test
    public void between(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄在90-99的用户,age:说明是根据年龄筛选,年龄在90-99
        queryWrapper.between("age",90,99);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  8. notBetween:不在…之间
    @Test
    public void notBetween(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄不在90-99的用户,age:说明是根据年龄筛选,年龄不在90
        queryWrapper.notBetween("age",90,99);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  9. in:包含…
    @Test
    public void in(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄是33,90,99的用户,age:说明是根据年龄筛选,年龄是33,90,99
        queryWrapper.in("age",90,99,33);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  10. notIn:不包含…
    @Test
    public void notIn(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //查询年龄不是1,2,3,4,5,6的用户,age:说明是根据年龄筛选,年龄不是1,2,3,4,5,6
        queryWrapper.notIn("age",1,2,3,4,5,6);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    

模糊查询

模糊查询 API 就是 SQL 当中的模糊查询

  1. like:包含传入的值。
    @Test
    public void like(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:like("age",33)--> age like %33%
        queryWrapper.like("age",33);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  2. notLike:不包含传入的值
    @Test
    public void notLike(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:notLike("age",3)--> age NOT LIKE %3%,年龄不包含3
        queryWrapper.notLike("age",3);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  3. likeLeft
    @Test
    public void likeLeft(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:likeLeft("age",3)--> age LIKE %3,年龄包含3
        queryWrapper.likeLeft("age",3);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  4. likeRight
    @Test
    public void likeRight(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:likeRight("age",3)--> age LIKE 3%,年龄以3开头
        queryWrapper.likeRight("age",3);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    

判空

  1. isNull:为空
    @Test
    public void isNull(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:isNull("age")-->查询age为空的字段
        queryWrapper.isNull("age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  2. isNotNull:不为空
    @Test
    public void isNotNull(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //模糊查询:isNotNull("age")-->查询age不为空的字段
        queryWrapper.isNotNull("age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    

排序

  1. orderBy:升序或者降序
    @Test
    public void orderBy(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //全部降序排列
        queryWrapper.orderBy(true,false,"age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    public void orderByAsc(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        全部升序排列
        queryWrapper.orderBy(true,true,"age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  2. orderAsc:全部升序排列
    @Test
    public void orderByAsc(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc(true,"age","name");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
  3. orderByDesc:全部降序排列
    @Test
    public void orderByDesc(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc(true,"age","name");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    

分组

  1. groupBy
    groupBy(true,"age");-->GROUP BY age 
    

逻辑查询

  1. or:调用OR,表示紧接下一个方法不是用and连接(不调用OR,则默认使用AND连接)

    @Test
    public void or(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",18).or().eq("name","zwq");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    SELECT id,create_time,name,update_time,email,age FROM user WHERE (name = ? OR name = ?) 
    
  2. and:and嵌套

    @Test
    public void and(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",18).and(wrapper->wrapper.eq("age",21).eq("email","zwq@qq.com"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    SELECT id,create_time,name,update_time,email,age FROM user WHERE (name = ? AND ( (age = ? AND email = ?) )) 
    

查询指定字段

在 MyBatis-Plus 查询时,默认查询所有的字段,如果有需要也可以通过 select 方法查询指定字段

  1. select:查询指定字段
    @Test
    public void select(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("age",21).or().eq("name","zwq").select("age","name","email");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }
    
    SELECT age,name,email FROM user WHERE (age = ? OR name = ?)
    

MyBatis-Plus 自动填充

  1. 自动填充功能主要用于新增一条数据或者修改一条数据的时候,对其中某些字段自动设置值,也就是不用我们在程序中显式设置。
  2. 在阿里手册数据库规范当中,明确要求每一张表都必须存在两个字段,一个就是创建时间create_time,另一个就是修改时间update_time,这两个字段如果我们需要手动设置值的话也非常容易,就是new Date()即可,假设一个项目上有几百张表,每张表都有这两个字段,一张表有多次修改的业务,这样如果让我们写这些没意义的代码只会降低开发效率,所以这时就可以使用 MyBatis-Plus 自动填充功能。

自动填充使用步骤

  1. 为实体类字段添加注解以及注解对应属性
    fill = FieldFill.INSERT:在 insert 的时候自动填充字段值
    fill = FieldFill.INSERT_UPDATE:在 insert 和 update 的时候自动填充字段值
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  2. 然后我们需要编写自动填充策略类,就是在 insert 或者 update 的时候,自动填充什么样的字段值,然后将给类交给 Spring 管理
    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        /**
         *  插入时,自动填充策略
         *  为指定字段决定在插入时,填充指定的默认值
         * @param metaObject
         */
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill...");
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    
        /**
         *  更新时,自动填充策略
         *  为指定字段决定在更新时,填充指定的默认值
         * @param metaObject
         */
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill...");
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
  3. 测试上面配置好的填充策略
    在这里插入图片描述

MyBatis-Plus 逻辑删除

  1. 在 delete 表数据的时候,有两钟情况,一是物理删除(真正从数据库表删除数据),二是逻辑删除,就是在删除的时候携带某些条件,比如一张表中新增一个字段(deleted,是否逻辑删除),如果逻辑删除了,就设置为1,如果没有逻辑删除,就设置为0,这样在查询的时候,只要携带deleted为查询条件即可,这样就避免了数据真正被删除。
  2. 在 MyBatis-Plus 当中,如果为一张表数据配置逻辑删除后,那么以后对这张表任意的记录进行删除操作的时候,都不会真正的删除,而且将deleted字段改为逻辑已删除对应的值。

未逻辑删除情况

在这里插入图片描述

逻辑删除使用步骤

  1. 修改表结构,添加deleted字段,用于表示数据是否被删除,1代表删除,0代表未删除。
    ALTER TABLE `user`ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除';
    
  2. 修改实体类,增加deleted属性并且添加@TableLogic注解
    @TableLogic
    private Integer deleted;
    
  3. 全局配置逻辑已删除值、逻辑未删除值
    mybatis-plus:
      global-config:
        db-config:
          logic-delete-value: 1 #逻辑删除之后设置的值为1
          logic-not-delete-value: 0 #未逻辑删除之后设置的值为0
    
  4. 测试逻辑删除功能
    在这里插入图片描述
  5. 再测试一下逻辑删除之后,查询是怎么样子的
    在这里插入图片描述

MyBatis-Plus 代码生成器

  1. MyBatis 也有代码生成器,MyBatis-Plus 也有代码生成器代码生成器,它们两者有什么区别?
    MyBatis 的代码生成器可以一次性根据所有数据库表生成代码,而 MyBatis-Plus 的代码生成器只能一次生成一张数据库表的代码,这就是两者区别之处。
  2. 如果想知道 MyBatis 怎么实现代码生成器,可以看下面一篇博客
    MyBatis 代码生成器详细讲解
  3. 因为文章篇幅的原因,我把 MyBatis-Plus 的代码生成器另外写一篇博客详细说明
    MyBatis-Plus 代码生成器详细讲解

总结

  1. 这篇文章肝了近两万字,相信看完之后,你绝对可以将 MyBatis-Plus 应用到实战当中。
  2. 如果大家觉得不错,可以点个赞或者关注博主我也行,之后会再接再厉输出好内容文章,感谢!

版权声明:本文为weixin_44176169原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。