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.快速开始
-
创建数据库mybatis_plus
-
创建表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) ); -
插入数据
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'); -
创建项目并初始化!!
-
导入依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency>注:尽量不要同时导入mybatis和mybatis-plus依赖,可能会产生冲突
-
配置数据源
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 -
在对应的UserMaaper接口上继承BaseMapper
@Repository public interface UserMapper extends BaseMapper<User> { }注:
- 不需要再去配置xml文件,所有基本的查询语句是在BaseMapper接口中了!!,基本的CRUD以已经实现,但是如果有特殊的sql语句,还是需要自己编写!!
- 需要在继承的BaseMaaper传入pojo的实体类泛型
-
测试
@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、插入

-
在插入数据的时候,如果主键id我们没有自己设定,那么会自动生成一个全局唯一的id,并且会自动回填到我们的对象里面,也就是会自动将我们原本没有设置id的对象的id赋值!!
-
主键生成策略:
-
参考博客:
-
UUID
-
自增ID
-
雪花算法(默认):使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0,每次生成唯一的值
-
Redis
-
zookeeper
-
-
配置主键生成策略:
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、更新

-
更新的时候虽然名字是ById,但是需要传入的是一个适配的泛型类对象,同时通过日志可以发现,mybatis-plus会根据条件自动帮我们拼接sql语句,即加入某一字段值为空,那么设置的时候就不会set该字段!!
-
自动填充
-
阿里巴巴开发手册表明,所有的数据库表都应该包含的两个字段:gmt_create,gmt_modified,也就是创建时间以及修改时间,而有关这个的操作,我们都应该使用自动化来完成!!
-
数据库级别操作(实际工作不被允许):使用默认值的方式
-
-
代码级别:
-
使用@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表示数据为长整数!!
-
-
-
乐观锁悲观锁
-
乐观锁:十分乐观,总是认为不会出现问题,无论干什么都不会上锁,如果出现问题再次进行各更新值测试!
-
悲观锁:十分悲观,总是认为干什么都会出现问题,无论干什么都会上锁,在去操作!
-
当要更新一条记录的时候,希望这条记录没有被别人更新,乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
- 说明:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1 newVersion会回写到entity中- 仅支持
updateById(id)与update(entity, wrapper)方法 - 在
update(entity, wrapper)方法下,wrapper不能复用!!!
-
实现乐观锁:
-
数据库添加字段version
-
实体类添加属性
@Version private Integer version; -
新建一个配置类配置插件
@Configuration @MapperScan("com.xiaoye.mapper")//可以不用放在启动类上,也可以放在这里 @EnableTransactionManagement//自动管理事务 public class MyBatisPlusConfig { //注册乐观锁插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } } -
测试!!
-
-
4.3、查询

-
常用查询:
- selectById():根据id查询
- selectList():查询全部
- selectBatchIds():批量查询,参数是一个集合
- selectByMap():条件查询,参数为map,map的键为字段名,值为字段值
-
分页查询:
-
在配置类下增加插件
/** * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除) */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor2() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } -
使用
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、删除

-
常用删除:
// userMapper.delete(); //批量删除 userMapper.deleteBatchIds(); //根据id删除 userMapper.deleteById(); //根据条件删除 userMapper.deleteByMap(); -
逻辑删除:在数据库中不直接删除,而是通过一个逻辑变量,比如is_delete=0|1,而不是真正的删除
补充说明:
- 只对自动注入的 sql 起效:
- 插入: 不作限制
- 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 删除: 转变为 更新
-
在数据库增加一个is_delete字段
-
在实体类中增加属性(3.3以后可以不用加注解)
@TableLogic private Integer isDelete; -
在yaml中配置
mybatis-plus: global-config: db-config: logic-delete-field: isDelete # 全局逻辑删除的实体字段名 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) -
测试!!
-
常见问题:
- 如何插入:
- 数据库定义默认值(推荐)
- 自己set
- 使用自动填充功能
- 删除接口功能自动填充功能失效:
- 使用
deleteById方法(推荐) - 使用
update方法并:UpdateWrapper.set(column, value)(推荐) - 使用
update方法并:UpdateWrapper.setSql("column=value")
- 使用
- 如何插入:
5.性能分析插件
该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长
-
导入依赖:
<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.2.0</version> </dependency> -
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 -
配置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 -
编写自定义日志类
@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 ""; } } -
注意:
- driver-class-name 为 p6spy 提供的驱动类
- url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
- 打印出 sql 为 null,在 excludecategories 增加 commit
- 批量操作不打印 sql,去除 excludecategories 中的 batch
- 批量操作打印重复的问题请使用 MybatisPlusLogFactory (3.2.1 新增)
- 该插件有性能损耗,不建议生产环境使用。
6.条件构造器Wrapper
-
QueryWrapper:select,delete使用
-
UpdateWrapper:update使用
-
常用的:
![]()
-
可以使用链式编程:
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.代码生成器
-
导入依赖
<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> -
编写自动生成代码类
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

浙公网安备 33010602011771号