第一章:了解Mybatis Plus(摘自官网)
1.1 Mybatis Plus介绍
- Mybatis Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。
- 官网。
愿景: 我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

1.2 代码以及文档
1.3 特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.4 架构

第二章:整合Mybatis Plus
2.1 准备工作
- MySQL 5.7版本以上。
- JDK 11。
- IDEA 2021+。
- Sql脚本:
-- 创建数据库CREATE DATABASE IF NOT EXISTS mpDEFAULT CHARACTER SET utf8mb4DEFAULT COLLATE utf8mb4_general_ci;
-- 创建测试表CREATE TABLE `tb_user` (`id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT COMMENT '主键ID',`user_name` VARCHAR ( 20 ) NOT NULL COMMENT '用户名',`password` VARCHAR ( 20 ) NOT NULL COMMENT '密码',`name` VARCHAR ( 30 ) DEFAULT NULL COMMENT '姓名',`age` INT ( 11 ) DEFAULT NULL COMMENT '年龄',`email` VARCHAR ( 50 ) DEFAULT NULL COMMENT '邮箱',PRIMARY KEY ( `id` )) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4;
-- 插入测试数据INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('1', 'zhangsan', '123456', '张三', '18', 'test1@qq.com');INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('2', 'lisi', '123456', '李四', '20', 'test2@qq.com');INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('3', 'wangwu', '123456', '王五', '28', 'test3@qq.com');INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('4', 'zhaoliu', '123456', '赵六', '21', 'test4@qq.com');INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES ('5', 'sunqi', '123456', '孙七', '24', 'test5@qq.com');
2.2 SpringBoot整合Mybatis Plus
2.2.1 导入相关依赖
- pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.1</version><relativePath/></parent><groupId>com.example</groupId><artifactId>mp</artifactId><version>0.0.1</version><name>mp</name><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
2.2.2 编写application.yml
- application.yml
# 服务器配置server:# 端口port: 8080servlet:# 项目名称context-path: /mp# tomcat的配置tomcat:# tomcat的URI编码uri-encoding: UTF-8# tomcat最大线程数,默认为200max-threads: 800# Tomcat启动初始化的线程数,默认值25min-spare-threads: 30max-http-header-size: 65536spring:# 配置数据源datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=trueusername: rootpassword: 123456# Hikari 连接池配置hikari:# 最小空闲连接数量minimum-idle: 5# 空闲连接存活最大时间,默认600000(10分钟)idle-timeout: 180000# 连接池最大连接数,默认是10maximum-pool-size: 1000# 此属性控制从池返回的连接的默认自动提交行为,默认值:trueauto-commit: true# 连接池名称pool-name: MPHikariCP# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟max-lifetime: 1800000# 数据库连接超时时间,默认30秒,即30000connection-timeout: 30000connection-test-query: SELECT 1data-source-properties:useInformationSchema: true# 开启日志mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.2.3 编写POJO
- User.java
package com.example.mp.domain;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author 许大仙* @version 1.0* @since 2021-06-17 21:01*/@Data@NoArgsConstructor@AllArgsConstructor@TableName("tb_user")public class User implements Serializable {/*** 主键*/private Long id;/*** 用户名*/@TableField("user_name")private String username;/*** 密码*/private String password;/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 电子邮件*/private String email;}
2.2.4 编写Mapper
- UserMapper.java
package com.example.mp.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.example.mp.domain.User;import org.apache.ibatis.annotations.Mapper;/*** @author 许大仙* @version 1.0* @since 2021-06-17 21:10*/@Mapperpublic interface UserMapper extends BaseMapper<User> {}
2.2.5 编写启动类
- MpApplication.java
package com.example.mp;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.example.mp.mapper") //设置mapper接口的扫描包public class MpApplication {public static void main(String[] args) {SpringApplication.run(MpApplication.class, args);}}
2.2.6 编写测试用例
- MpApplicationTests.java
package com.example.mp;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTestclass MpApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect() {List<User> userList = userMapper.selectList(null);userList.forEach(System.out::println);}}
第三章:通用CRUD
3.1 概述
- 通过上面的学习,我们知道通过继承BaseMapper可以实现各种各样的单表操作,下面将详细讲解这些操作。

3.2 插入操作
3.2.1 方法定义
- 方法:
/*** 插入一条记录** @param entity 实体对象*/int insert(T entity);
3.2.2 测试用例
- 示例:
package com.example.mp;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestclass MpApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testInsert(){User user = new User();user.setUsername("zhangsan");user.setPassword("123456");user.setName("张三");user.setAge(18);user.setEmail("123456@qq.com");int count = userMapper.insert(user);System.out.println("count = " + count); //数据库受影响的行数System.out.println("user.getId() = " + user.getId()); //自增后的id会回填到对象中}}
3.2.3 测试
- 结果:

- 数据库记录:

- 可以看到,数据已经写入到数据库中了,但是,id的值是不正确的,我们期望的是数据库自增长,实际是MP生成了id的值写入到数据库中。
- MP支持的id策略:
package com.baomidou.mybatisplus.annotation;import lombok.Getter;/*** 生成ID类型枚举类** @author hubin* @since 2015-11-10*/@Getterpublic enum IdType {/*** 数据库ID自增* <p>该类型请确保数据库设置了 ID自增 否则无效</p>*/AUTO(0),/*** 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)*/NONE(1),/*** 用户输入ID* <p>该类型可以通过自己注册自动填充插件进行填充</p>*/INPUT(2),/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 *//*** 分配ID (主键类型为number或string),* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)** @since 3.3.0*/ASSIGN_ID(3),/*** 分配UUID (主键类型为 string)* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))*/ASSIGN_UUID(4);private final int key;IdType(int key) {this.key = key;}}
- 修改User对象:
package com.example.mp.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.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author 许大仙* @version 1.0* @since 2021-06-17 21:01*/@Data@NoArgsConstructor@AllArgsConstructor@TableName("tb_user")public class User implements Serializable {/*** 主键*/@TableId(type= IdType.AUTO)private Long id;/*** 用户名*/@TableField("user_name")private String username;/*** 密码*/private String password;/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 电子邮件*/private String email;}
- 数据插入成功:

3.2.4 @TableField注解
- 在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:
- ①对象中的属性名和字段名不一致的问题(非驼峰)。
- ②对象中的属性字段在表中不存在的问题。
- 示例:
package com.example.mp.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.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author 许大仙* @version 1.0* @since 2021-06-17 21:01*/@Data@NoArgsConstructor@AllArgsConstructor@TableName("tb_user")public class User implements Serializable {@TableId(type= IdType.AUTO)private Long id;@TableField("user_name") //解决字段名不一致private String username;@TableField(select = false) //该字段不加入到查询中private String password;private String name;private Integer age;private String email;@TableField(exist = false)private String address; //该字段在数据库表中不存在}
3.3 更新操作
3.3.1 概述
- 在MP中,更新操作有2种,一种是根据id更新,另一种是根据条件更新。
3.3.2 根据id更新
- 方法定义:
/*** 根据 ID 修改** @param entity 实体对象(对象属性可以为null)*/int updateById(@Param(Constants.ENTITY) T entity);
- 测试:
package com.example.mp;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 19:22*/@SpringBootTestpublic class TestUpdate {@Autowiredprivate UserMapper userMapper;@Testpublic void testUpdateById() {User user = new User();user.setId(1L);user.setUsername("许大仙");//根据id更新,更新不为null的字段,返回更新的条数int result = userMapper.updateById(user);System.out.println("result = " + result);}}
- 结果:

- 数据库记录:

3.3.3 根据条件更新
- 方法定义:
/*** 根据 whereEntity 条件,更新记录** @param entity 实体对象 (set 条件值,可以为 null)* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)*/int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.Wrapper;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 19:22*/@SpringBootTestpublic class TestUpdate {@Autowiredprivate UserMapper userMapper;@Testpublic void testUpdate() {//封装更新字段的对象User user = new User();user.setAge(18);//封装更新条件的对象QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("user_name", "zhangsan");int result = userMapper.update(user, queryWrapper);System.out.println("result = " + result);}@Testpublic void testUpdate2() {UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.set("age", 18).set("password", "123456").eq("user_name", "zhangsan");int result = userMapper.update(null, updateWrapper);System.out.println("result = " + result);}}
- 结果:

- 数据库记录:

3.4 删除操作
3.4.1 deleteById
- 方法定义:
/*** 根据 ID 删除** @param id 主键ID*/int deleteById(Serializable id);
- 测试:
package com.example.mp;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 20:04*/@SpringBootTestpublic class TestDeleteById {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){//执行删除操作int result = userMapper.deleteById(6);System.out.println("result = " + result);}}
- 结果:

3.4.2 deleteByMap
- 方法定义:
/*** 根据 columnMap 条件,删除记录** @param columnMap 表字段 map 对象*/int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
- 测试:
package com.example.mp;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.HashMap;import java.util.Map;/*** @author 许大仙* @version 1.0* @since 2021-06-18 20:10*/@SpringBootTestpublic class TestDeleteByMap {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){Map<String,Object> map = new HashMap<>();map.put("id", "5");map.put("age", "24");//将map中的元素设置为删除的条件,多个条件之间是and的关系int result = userMapper.deleteByMap(map);System.out.println("result = " + result);}}
- 结果:

3.4.3 delete
- 方法定义:
/*** 根据 entity 条件,删除记录** @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)*/int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 20:31*/@SpringBootTestpublic class TestDelete {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.gt("age", 20);userMapper.delete(queryWrapper);}}
- 结果:

3.4.4 deleteBatchIds
- 方法定义:
/*** 删除(根据ID 批量删除)** @param idList 主键ID列表(不能为 null 以及 empty)*/int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
- 测试:
package com.example.mp;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-18 20:39*/@SpringBootTestpublic class TestDeleteBatchIds {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {//根据id集合批量删除int result = userMapper.deleteBatchIds(List.of(1, 2, 3));System.out.println("result = " + result);}}
- 结果:

3.5 查询操作
3.5.1 概述
- MP提供了多种查询操作,包括根据id查询、批量查询、查询单条数据、查询列表、分页查询等操作。
3.5.2 selectById
- 方法定义:
/*** 根据 ID 查询** @param id 主键ID*/T selectById(Serializable id);
- 示例:
package com.example.mp;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 21:00*/@SpringBootTestpublic class TestSelectById {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){//根据id查询数据User user = userMapper.selectById(1);System.out.println("user = " + user);}}
- 结果:

3.5.3 selectBatchIds
- 方法定义:
/*** 查询(根据ID 批量查询)** @param idList 主键ID列表(不能为 null 以及 empty)*/List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
- 测试:
package com.example.mp;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-18 22:01*/@SpringBootTestpublic class TestSelectBatchIds {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {//根据id集合批量查询List<User> userList = userMapper.selectBatchIds(List.of(1, 2, 3));System.out.println("userList = " + userList);}}
- 结果:

3.5.4 selectOne
- 方法定义:
/*** 根据 entity 条件,查询一条记录** @param queryWrapper 实体对象封装操作类(可以为 null)*/T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 22:08*/@SpringBootTestpublic class TestSelectOne {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(User::getUsername, "zhangsan");//根据条件查询一条数据,如果结果超过一条则报错User user = userMapper.selectOne(lambdaQueryWrapper);System.out.println("user = " + user);}}
- 结果:

3.5.5 selectList
- 方法定义:
/*** 根据 entity 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)*/List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-18 22:14*/@SpringBootTestpublic class TestSelectList {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.ge(User::getId, 3);//根据条件查询数据List<User> userList = userMapper.selectList(lambdaQueryWrapper);System.out.println("userList = " + userList);}}
- 结果:

3.5.6 selectCount
- 方法定义:
/*** 根据 Wrapper 条件,查询总记录数** @param queryWrapper 实体对象封装操作类(可以为 null)*/Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;/*** @author 许大仙* @version 1.0* @since 2021-06-18 22:21*/@SpringBootTestpublic class TestSelectCount {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {//根据条件查询数据条数Integer count = userMapper.selectCount(new LambdaQueryWrapper<User>().ge(User::getId, 2));System.out.println("count = " + count);}}
- 结果:

3.5.7 selectPage
- 方法定义:
/*** 根据 entity 条件,查询全部记录(并翻页)** @param page 分页查询条件(可以为 RowBounds.DEFAULT)* @param queryWrapper 实体对象封装操作类(可以为 null)*/<P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
- 配置分页插件:
package com.example.mp.config;import com.baomidou.mybatisplus.annotation.DbType;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 许大仙* @version 1.0* @since 2021-06-18 22:42*/@Configurationpublic class MybatisPlusConfig {// 配置分页插件@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}
- 测试:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-18 22:36*/@SpringBootTestpublic class TestSelectPage {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){Page<User> page = new Page<>(1,5);Page<User> selectPage = userMapper.selectPage(page, new QueryWrapper<>());long total = selectPage.getTotal();long pages = selectPage.getPages();System.out.println("数据总条数 = " + total);System.out.println("总页数 = " + pages);List<User> userList = selectPage.getRecords();System.out.println("userList = " + userList);}}
- 结果:

3.6 SQL注入原理
- 我们知道,MP在启动后会将
BaseMapper中的一系列的方法注册到MappedStatement中,那么是如何注入的?流程是什么? - 在MP中,
ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类,实现关系如下:

- 在
AbstractSqlInjector中,主要是由inspectInject方法进行注入的,源码如下:
package com.baomidou.mybatisplus.core.injector;import com.baomidou.mybatisplus.core.mapper.Mapper;import com.baomidou.mybatisplus.core.metadata.TableInfo;import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;import org.apache.ibatis.builder.MapperBuilderAssistant;import org.apache.ibatis.logging.Log;import org.apache.ibatis.logging.LogFactory;import java.util.List;import java.util.Set;/*** SQL 自动注入器** @author hubin* @since 2018-04-07*/public abstract class AbstractSqlInjector implements ISqlInjector {private static final Log logger = LogFactory.getLog(AbstractSqlInjector.class);@Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);if (modelClass != null) {String className = mapperClass.toString();Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());if (!mapperRegistryCache.contains(className)) {List<AbstractMethod> methodList = this.getMethodList(mapperClass);if (CollectionUtils.isNotEmpty(methodList)) {TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);// 循环注入自定义方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));} else {logger.debug(mapperClass.toString() + ", No effective injection method was found.");}mapperRegistryCache.add(className);}}}/*** <p>* 获取 注入的方法* </p>** @param mapperClass 当前mapper* @return 注入的方法集合* @since 3.1.2 add mapperClass*/public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass);}
- 在实现方法中,
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));是关键,循环遍历方法,进行注入。 - 查看
m.inject方法,可以看到如下的代码:
public abstract class AbstractMethod implements Constants {protected static final Log logger = LogFactory.getLog(AbstractMethod.class);protected Configuration configuration;protected LanguageDriver languageDriver;protected MapperBuilderAssistant builderAssistant;/*** 注入自定义方法*/public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {this.configuration = builderAssistant.getConfiguration();this.builderAssistant = builderAssistant;this.languageDriver = configuration.getDefaultScriptingLanguageInstance();/* 注入自定义方法 */injectMappedStatement(mapperClass, modelClass, tableInfo);}....}
- 由此可知,最终是通过调用
injectMappedStatement进行真正的注入的。
/*** 注入自定义 MappedStatement** @param mapperClass mapper 接口* @param modelClass mapper 泛型* @param tableInfo 数据库表反射信息* @return MappedStatement*/public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
injectMappedStatement方法的实现如下:

- 以
SelectById为例查看:
public class SelectById extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo, false),tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true, true)), Object.class);return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);}}
- 可以看到,生成了
SqlSource对象,再将SQL通过addSelectMappedStatementForTable方法添加到了MappedStatement中。
第四章:Mybatis Plus的配置
4.1 概述
- 在MP中有大量的配置,其中有一部分是Mybatis原生的配置,另一部分是MP的配置。
4.2 基本配置
4.2.1 configLocation
- MyBatis配置文件位置,如果有单独的MyBatis配置,将其路径配置到
configLocation中。 - SpringBoot:
mybatis-plus:config-location: classpath:mybatis-config.xml
4.2.2 mapperLocations
- Mybatis Mapper所对应的XML文件配置,如果在Mapper中有自定义方法(XML中有自定义实现),需要进行该配置,告诉Mapper所对应的XML文件位置。
- SpringBoot:
mybatis-plus:mapper-locations: classpath*:mybatis/*.xml
4.2.3 typeAliasesPackage
- Mybatis别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在Mapper对应的XML文件中可以直接使用类名,而不用使用全限定的类名(即XML中调用的时候不用包含包名)。
- SpringBoot:
mybatis-plus:type-aliases-package: com.example.mp.domain
4.3 进阶配置
4.3.1 mapUnderscoreToCamelCase
- mapUnderscoreToCamelCase
- 类型:boolean。
- 默认值:true。
是否开启自动驼峰命名规则的映射,即从经典数据库列名A_COLUMN(下划线命名)到经典的Java属性名AColumn的类似映射。
注意:
- 此属性在Mybatis中的原默认值为false,在MybatisPlux中,此属性也将用于生成最终的SQL的select body。
- 如果数据库命名符合规则则无需使用@TableField注解指定数据库字段名。
SpringBoot:
mybatis-plus:configuration:#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在,默认值为true,一般情况下,默认即可map-underscore-to-camel-case: false
4.3.2 cacheEnabled
- cacheEnabled:
- 类型:boolean。
- 默认值:true。
- 全局的开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为true。
- SpringBoot:
mybatis-plus:configuration:# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true,一般情况下,默认即可cacheEnabled: false
4.4 DB策略配置
4.4.1 idType
- idType:
- 类型:com.baomidou.mybatisplus.annotation.IdType。
- 默认值:ASSIGN_ID。
- 全局设置主键类型,设置后,即可省略实体对象中的
@TableId(type=IdType.AUTO)配置。 - SpringBoot:
mybatis-plus:global-config:db-config:id-type: auto
4.4.2 tablePrefix
- tablePrefix:
- 类型:String。
- 默认值:null。
- 表名前缀,全局配置后可省略
@TableName()的配置。 - SpringBoot:
mybatis-plus:global-config:db-config:table-prefix: tb_
第五章:条件构造器
5.1 概述
- 在MP中,
Wrapper接口的实现类关系如下:

- 可以看到,
AbstractWrapper和AbstractChainWrapper是重点实现,我们重点学习AbstractWrapper及其实现子类。
说明:QueryWrapper(LambdaQueryWrapper)和UpdateWrapper(LambdaUpdateWrapper)的父类用于生成sql的where条件, entity属性也用于生成sql的where条件。 注意:entity生成的where条件与使用各个api生成的where条件没有任何关联行为。
5.2 allEq
5.2.1 说明
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
参数说明:
- params:key是数据库字段名,value为字段值。
- null2IsNull:为true则在map的value为null时调用isNull方法,为false时则忽略value为null。
5.2.2 应用示例
- 示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.HashMap;import java.util.List;import java.util.Map;/*** @author 许大仙* @version 1.0* @since 2021-06-19 11:04*/@SpringBootTestpublic class TestAllEq {@Autowiredprivate UserMapper userMapper;@Testpublic void testAllEq() {QueryWrapper<User> wrapper = new QueryWrapper<>();//设置条件Map<String,Object> params = new HashMap<>();params.put("name","zhangsan");params.put("age", null);//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ? AND age IS NULL)wrapper.allEq(params);List<User> userList = userMapper.selectList(wrapper);System.out.println("userList = " + userList);}@Testpublic void testAllEqNull2IsNullFalse() {QueryWrapper<User> wrapper = new QueryWrapper<>();//设置条件Map<String,Object> params = new HashMap<>();params.put("name","zhangsan");params.put("age", null);//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ?)wrapper.allEq(params,false);List<User> userList = userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
5.3 基本操作比较
- eq:等于
= - ne:不等于
<> - gt:大于
> - lt:小于
< - ge:大于等于
>= - le:小于等于
le - between:
between 值1 AND 值2 - notBetween:
NOT between 值1 AND 值2 - in:
字段 IN (值1,值2,...) - notIn:
字段 NOT IN (值1,值2,...)
- 示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-19 11:20*/@SpringBootTestpublic class TestEq {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("password", "123456").ge("age", 20).in("name", "张三", "李四", "王五");List<User> userList = userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
5.4 模糊查询
- like:
- LIKE ‘%值%’。
- 例:
like("name", "王")—->name like '%王%'
- notlike:
- NOT LIKE ‘%值%’。
- 例:
notLike("name", "王")—->name not like '%王%'
- likeLeft:
- LIKE ‘%值’。
- 例:
likeLeft("name", "王")—->name like '%王'
- LikeRight:
- LIKE ‘值%’。
- 例:
likeRight("name", "王")—->name like '王%'
- 示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-19 11:27*/@SpringBootTestpublic class TestLike {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {QueryWrapper<User> wrapper = new QueryWrapper<>();//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name LIKE %张%)wrapper.like("name", "张");List<User> userList = userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
5.5 排序
- orderBy:
- 排序:ORDER BY 字段, …
- 例:
orderBy(true, true, "id", "name")—->order by id ASC,name ASC
- orderByAsc:
- 排序:ORDER BY 字段, … ASC。
- 例:
orderByAsc("id", "name")—->order by id ASC,name ASC
- orderByDesc:
- 排序:ORDER BY 字段, … DESC。
- 例:
orderByDesc("id", "name")—->order by id DESC,name DESC
- 示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-19 13:40*/@SpringBootTestpublic class TestOrderBy {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {QueryWrapper<User> wrapper = new QueryWrapper<>();//SELECT id,user_name,password,name,age,email FROM tb_user ORDER BY age ASCwrapper.orderByAsc("age");List<User> userList = userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
5.6 逻辑查询
- or:
- 拼接 OR。
- 例:
eq("id",1).or().eq("name","老王")—->id = 1 or name = '老王'
- and:
- AND 嵌套。
- 例:
and(i -> i.eq("name", "李白").ne("status", "活着"))—->and (name = '李白' and status <> '活着')
- 示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-19 13:44*/@SpringBootTestpublic class TestOrAnd {@Autowiredprivate UserMapper userMapper;@Testpublic void test(){QueryWrapper<User> wrapper = new QueryWrapper<>();//SELECT id,user_name,password,name,age,email FROM tb_user WHERE (name = ? OR age = ?)wrapper.eq("name", "张三").or().eq("age", 18);List<User> userList = this.userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
5.7 select
select:设置查询字段。
示例:
package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.mp.domain.User;import com.example.mp.mapper.UserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;/*** @author 许大仙* @version 1.0* @since 2021-06-19 13:48*/@SpringBootTestpublic class TestSelect {@Autowiredprivate UserMapper userMapper;@Testpublic void test() {QueryWrapper<User> wrapper = new QueryWrapper<>();//SELECT user_name,age,password FROM tb_user WHERE (name = ? OR age = ?)wrapper.eq("name", "张三").or().eq("age", 18).select("user_name", "age", "password");List<User> userList = this.userMapper.selectList(wrapper);System.out.println("userList = " + userList);}}
