认识MyBatis的好兄弟 MyBatis – Plus

  • 2019 年 10 月 4 日
  • 筆記

快速搭建

我们先通过一个简单的Demo 来认识一下 MyBatis – Plus 的搭建和用法:

现在数据库中创建一张 User 表,表结构和数据如下

id

name

age

email

1

lx

21

2

cxuan

20

3

liux

28

4

xiaona

21

5

lxuan

24

对应的建表语句和SQL语句如下

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)  );    INSERT INTO `user` VALUES (1, 'lx', 21, '[email protected]');  INSERT INTO `user` VALUES (2, 'cxuan', 20, '[email protected]');  INSERT INTO `user` VALUES (3, 'liux', 28, '[email protected]');  INSERT INTO `user` VALUES (4, 'xiaona', 21, '[email protected]');  INSERT INTO `user` VALUES (5, 'lxuan', 24, '[email protected]');  

使用 IDEA 新建一个 SpringBoot 项目,构建完成后,在 pom.xml添加如下依赖:

<dependencies>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter</artifactId>    </dependency>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-test</artifactId>      <scope>test</scope>    </dependency>    <dependency>      <groupId>org.projectlombok</groupId>      <artifactId>lombok</artifactId>      <optional>true</optional>    </dependency>    <dependency>      <groupId>com.baomidou</groupId>      <artifactId>mybatis-plus-boot-starter</artifactId>      <version>3.2.0</version>    </dependency>    <dependency>      <groupId>mysql</groupId>      <artifactId>mysql-connector-java</artifactId>      <version>5.1.38</version>    </dependency>  </dependencies>  

新建 application.yml ,在yml 文件上添加如下数据库驱动信息

spring:    datasource:      driver-class-name: com.mysql.jdbc.Driver      url: jdbc:mysql://localhost:3306/mybatis      username: root      password: 123456  

在主方法上添加 @MapperScan 注解,扫描mapper 包下面的内容

@SpringBootApplication  @MapperScan("com.mybatis.plus.mapper")  public class MybatisPlusApplication {        public static void main(String[] args) {          SpringApplication.run(MybatisPlusApplication.class, args);      }    }  

新建一个 User 类,使用 @Data lombok 中的注解,如下

@Data  public class User {        private long id;      private String name;      private Integer age;      private String email;  }  

然后新建一个接口,这个接口实现 BaseMapper接口

public interface UserMapper extends BaseMapper<User> {}  

使用单元测试进行测试:

@RunWith(SpringRunner.class)  @SpringBootTest  public class MybatisPlusApplicationTests {        @Autowired      private UserMapper userMapper;        @Test      public void contextLoads() {          System.out.println("----- selectAll method test -----");          List<User> userList = userMapper.selectList(null);          Assert.assertEquals(5,userList.size());          userList.forEach(System.out::println);      }    }  

输出:

User(id=1, name=lx, age=21, [email protected]) User(id=2, name=cxuan, age=20, [email protected]) User(id=3, name=liux, age=28, [email protected]) User(id=4, name=xiaona, age=21, [email protected]) User(id=5, name=lxuan, age=24, [email protected])

通过以上几个简单的步骤,我们就实现了 User 表的 查询 功能,你没有看到一条 SQL 语句和 映射的编写。

从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。

特性

  • 无侵入: 只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小: 启动即会自动注入基本 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、SQLServer2005、SQLServer 等多种数据库
  • 内置性能分析插件: 可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件: 提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

MyBatis 核心功能

Mapper CRUD 接口

“说明:

  • 通用 CRUD 封装BaseMapper[1]接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定,每一张表都有自己的唯一 id 主键
  • 对象 Wrapper条件构造器[2]

看下其基本使用

Insert

insert 只有一种用法,直接插入对象的实体

/**   * 插入一条记录   * @param entity 实体对象   * @return 插入成功记录数   */  int insert(T entity);  

验证

我们先为 User 实体类添加 @AllArgsConstructor 注解,如果 Lombok 不熟的同学请移步至 这些极简的注解你都清楚吗 这篇文章一探究竟。

下面是测试所用的代码

@Test  public void insertUserEntity(){    userMapper.insert(new User(6,"cxuanxuan",25,"[email protected]"));  }  

“注意 Junit 中测试的 insert 插入方法是没有返回值的,不然会报错,所以返回类型是void 。但其实 insert 方法是有返回值的,返回的是插入成功的记录数。

Delete

delete 有四种用法,分别是根据 id 删除、通过某个表字段删除、通过实体类删除和批量删除,比我们最初只是通过 id 删除功能强大很多。

/**   * 根据 ID 删除   * @param id 主键ID   * @return 删除成功记录数   */  int deleteById(Serializable id);    /**   * 根据 columnMap 条件,删除记录   * @param columnMap 表字段 map 对象   * @return 删除成功记录数   */  int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);    /**   * 根据 entity 条件,删除记录   * @param wrapper 实体对象封装操作类(可以为 null)   * @return 删除成功记录数   */  int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);    /**   * 删除(根据ID 批量删除)   * @param idList 主键ID列表(不能为 null 以及 empty)   * @return 删除成功记录数   */  int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);  

验证

验证示例如下:

// 根据某个特定的 id 进行删除。  @Test  public void deleteUserById(){    userMapper.deleteById(6);  }    // deleteByMap 参数是 Map<String, Object> 类型  // 第一个范型是字段名称,第二个范型是字段对应的值  @Test  public void deleteByColumnMap(){    Map<String,Object> columnMap = new HashMap<>();    columnMap.put("id",4);    userMapper.deleteByMap(columnMap);  }    // 使用 QueryWrapper 进行条件筛选  @Test  public void deleteEntity(){    QueryWrapper<User> queryWrapper = new QueryWrapper();    // 删除 name 不为 null ,并且 age 大于等于 28的记录    queryWrapper.isNotNull("name").ge("age",28);    userMapper.delete(queryWrapper);  }    // 根据 Collection 集合进行删除,也可以是 Collection 的子类  @Test  public void deleteBatch(){    List list = new ArrayList();    // 批量删除 1 和 2 的数据    list.add(1);    list.add(2);    userMapper.deleteBatchIds(list);  }  

上面是 Delete 方法的测试例子,其中 **deleteByColumnMap 和 deleteEntity ** 方法如果不传任何值,默认删除表中所有的数据。deleteBatch 如果传递 null 的话,测试会报错,无法通过单元测试。

Update

Update 更新方法只有两种形式,一种是直接根据 id 修改实体属性的值,一种是直接更新实体类

/**   * 根据 ID 修改   * @param entity 实体对象   * @return 修改成功记录数   */  int updateById(@Param(Constants.ENTITY) T entity);    /**   * 根据 whereEntity 条件,更新记录   * @param entity        实体对象 (set 条件值,可为 null)   * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)   * @return 修改成功记录数   */  int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);  

验证

// 根据id更新User表  @Test  public void updateUserById(){    userMapper.updateById(new User(5,"cxuaner",25,"[email protected]"));  }    // 更新 User 实体类信息  @Test  public void updateUserEntity(){    UpdateWrapper<User> updateWrapper = new UpdateWrapper();    updateWrapper.eq("age",28);    userMapper.update(new User(3,"cxuan",22,"[email protected]"),updateWrapper);  }  
  • 第一条 updateUserById 语句是根据 id 进行更新,传入一个实体类的意思是更新 id = 5的这条记录。 传入 null 会报错,如果没有 id = 5的这条记录,测试会成功,但是不会更新任何记录
  • 第二条 updateUserEntity 语句是根据实体类的条件进行更新,UpdateWrapper 是一个条件构造器,测试的是 age 为 28 的这条记录进行更新。 条件构造器后面会进行更加详细的介绍。

Select

Select 用法比较多,现在来一起看一下Select 有哪些用法

/**   * 根据 ID 查询   * @param id 主键ID   * @return 实体   */  T selectById(Serializable id);    /**   * 查询(根据ID 批量查询)   * @param idList 主键ID列表(不能为 null 以及 empty)   * @return 实体集合   */  List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);    /**   * 查询(根据 columnMap 条件)   * @param columnMap 表字段 map 对象   * @return 实体集合   */  List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);    /**   * 根据 entity 条件,查询一条记录   * @param queryWrapper 实体对象   * @return 实体   */  T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 Wrapper 条件,查询总记录数   * @param queryWrapper 实体对象   * @return 满足条件记录数   */  Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 entity 条件,查询全部记录   * @param queryWrapper 实体对象封装操作类(可以为 null)   * @return 实体集合   */  List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 Wrapper 条件,查询全部记录   * @param queryWrapper 实体对象封装操作类(可以为 null)   * @return 字段映射对象 Map 集合   */  List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 Wrapper 条件,查询全部记录   * 注意:只返回第一个字段的值   * @param queryWrapper 实体对象封装操作类(可以为 null)   * @return 字段映射对象集合   */  List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 entity 条件,查询全部记录(并翻页)   * @param page         分页查询条件(可以为 RowBounds.DEFAULT)   * @param queryWrapper 实体对象封装操作类(可以为 null)   * @return 实体分页对象   */  IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);    /**   * 根据 Wrapper 条件,查询全部记录(并翻页)   * @param page         分页查询条件   * @param queryWrapper 实体对象封装操作类   * @return 字段映射对象 Map 分页对象   */  IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);  

验证

下面就对上面的示例方法进行验证

/**  * 查询id = 1 的一条记录  */  @Test  public void selectById(){    User user = userMapper.selectById(1);    System.out.println("user = " + user);  }    /**  * 批量查询id = 1,2,3 的记录  */  @Test  public void selectUserBatchId(){    List batchIds = new ArrayList();    batchIds.add(1);    batchIds.add(2);    batchIds.add(3);    List list = userMapper.selectBatchIds(batchIds);    System.out.println("list = " + list);  }    /*  * 查询年龄为 21 岁的记录,并循环遍历输出  */  @Test  public void selectUserByMap(){    Map<String,Object> userMap = new HashMap<>();    userMap.put("age",21);    List<User> users = userMapper.selectByMap(userMap);    users.forEach(p-> {      System.out.println(p);    });  }    /**  * 根据 entity 条件,查询一条id = 1 的记录,需要手动添加 User(long id)的构造器  */  @Test  public void selectOneUser(){      // 需要在 User 类上添加一个 id 的构造函数    User user = userMapper.selectOne(new QueryWrapper<>(new User(1)));    System.out.println("user = " + user);  }    /**  * 根据 Wrapper 条件,查询年龄大于 22 的总条目数  */  @Test  public void selectCountNum(){    QueryWrapper<User> queryWrapper = new QueryWrapper();    queryWrapper.ge("age",22);    Integer integer = userMapper.selectCount(queryWrapper);    System.out.println("查询出来的总记录数 = " + integer);  }    /*  * 查询年龄大于22 的所有记录数,返回一个List  */  @Test  public void selectUserList(){    QueryWrapper<User> queryWrapper = new QueryWrapper();    queryWrapper.ge("age",22);    List<User> users = userMapper.selectList(queryWrapper);    users.forEach(p-> {      System.out.println(p);    });  }    /*  * 查询年龄大于22 的所有记录数,返回的是一个List<Map>  */  @Test  public void selectUserMaps(){    QueryWrapper<User> queryWrapper = new QueryWrapper();    queryWrapper.ge("age",22);    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);    maps.forEach(p-> {      System.out.println(p);    });  }    /**  * 查询年龄大于22 的所有记录数,返回的是一个List<Object>  * 注意:只返回第一个字段的值  */  @Test  public void selectUserObject(){    QueryWrapper<User> queryWrapper = new QueryWrapper();    queryWrapper.ge("age",22);    List<Object> objects = userMapper.selectObjs(queryWrapper);    objects.forEach(p-> {      System.out.println(p);    });  }  

参考资料

[1]

BaseMapper: https://gitee.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java

[2]

条件构造器: https://mp.baomidou.com/guide/wrapper.html