MyBatis-Plus简介

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

特性

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

开始使用

通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能

现有一张 User 表,其表结构如下:

  1. DROP TABLE IF EXISTS user;
  2. CREATE TABLE user
  3. (
  4. id BIGINT(20) NOT NULL COMMENT '主键ID',
  5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
  7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
  8. PRIMARY KEY (id)
  9. );

其对应的数据库 Data 脚本如下:

  1. DELETE FROM user;
  2. INSERT INTO user (id, name, age, email) VALUES
  3. (1, 'Jone', 18, 'test1@baomidou.com'),
  4. (2, 'Jack', 20, 'test2@baomidou.com'),
  5. (3, 'Tom', 28, 'test3@baomidou.com'),
  6. (4, 'Sandy', 21, 'test4@baomidou.com'),
  7. (5, 'Billie', 24, 'test5@baomidou.com');

pom.xml 如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.4.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>demo-mybatis-plus</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>demo-mybatis-plus</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>mysql</groupId>
  26. <artifactId>mysql-connector-java</artifactId>
  27. <scope>runtime</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.projectlombok</groupId>
  31. <artifactId>lombok</artifactId>
  32. <optional>true</optional>
  33. </dependency>
  34. <dependency>
  35. <groupId>com.baomidou</groupId>
  36. <artifactId>mybatis-plus-boot-starter</artifactId>
  37. <version>3.4.0</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-test</artifactId>
  42. <scope>test</scope>
  43. <exclusions>
  44. <exclusion>
  45. <groupId>org.junit.vintage</groupId>
  46. <artifactId>junit-vintage-engine</artifactId>
  47. </exclusion>
  48. </exclusions>
  49. </dependency>
  50. </dependencies>
  51. <build>
  52. <plugins>
  53. <plugin>
  54. <groupId>org.springframework.boot</groupId>
  55. <artifactId>spring-boot-maven-plugin</artifactId>
  56. </plugin>
  57. </plugins>
  58. </build>
  59. </project>

添加配置
application.yml

  1. # 数据源配置
  2. spring:
  3. datasource:
  4. username: root
  5. password: root123
  6. url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8
  7. driver-class-name: com.mysql.cj.jdbc.Driver
  8. # MyBatis-plus配置
  9. mybatis-plus:
  10. mapper-locations: classpath*:mapper/*Mapper.xml
  11. type-aliases-package: com.example.demo.dataobject

在 Spring Boot配置类中添加 @MapperScan 注解,扫描 Mapper 包:

  1. @SpringBootApplication
  2. @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
  3. public class Application {
  4. public static void main(String[] args) {
  5. SpringApplication.run(QuickStartApplication.class, args);
  6. }
  7. }

编写实体类 User.java(此处使用了 Lombok 简化代码)

  1. @Data
  2. public class User {
  3. private Long id;
  4. private String name;
  5. private Integer age;
  6. private String email;
  7. }

编写Mapper类 UserMapper.java

  1. public interface UserMapper extends BaseMapper<User> {
  2. }

添加测试类,进行功能测试:

  1. @SpringBootTest
  2. public class SampleTest {
  3. @Autowired
  4. private UserMapper userMapper;
  5. @Test
  6. public void testSelect() {
  7. System.out.println(("----- selectAll method test ------"));
  8. List<User> userList = userMapper.selectList(null);
  9. Assert.assertEquals(5, userList.size());
  10. userList.forEach(System.out::println);
  11. }
  12. }

主键生成策略

MyBatis-Plus如果不做任何主键策略配置,默认使用的是雪花算法。

snowflake算法是Twitter开源的分布式ID生成算法,结果是一个long类型的ID 。其核心思想:使用41bit作为毫秒数,10bit作为机器的ID(5bit数据中心,5bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每个毫秒可以产生4096个ID),最后还有一个符号位,永远是0。

针对主键设置主键策略使用注解方式为

  1. @TableId(value = "id", type = IdType.ID_WORKER)
  2. private Long id;

自动填充功能

创建时间修改时间 这样的表字段是不需要手动插入, 可以设置为自动填充

在需要自动填充的字段上添加注解:

@TableField(fill = FieldFill.INSERT)

  1. @Data
  2. public class User {
  3. private Long id;
  4. private String name;
  5. private Integer age;
  6. private String email;
  7. @TableField(fill = FieldFill.INSERT)
  8. private Date createTime;
  9. @TableField(fill = FieldFill.INSERT_UPDATE)
  10. private Date updateTime;
  11. }

自定义实现类 MyMetaObjectHandler

  1. @Slf4j
  2. @Component
  3. public class MyMetaObjectHandler implements MetaObjectHandler {
  4. @Override
  5. public void insertFill(MetaObject metaObject) {
  6. this.setFieldValByName("createTime",new Date(),metaObject);
  7. this.setFieldValByName("updateTime",new Date(),metaObject);
  8. }
  9. @Override
  10. public void updateFill(MetaObject metaObject) {
  11. this.setFieldValByName("updateTime",new Date(),metaObject);
  12. }
  13. }

注意事项:

  • 填充原理是直接给entity的属性设置值!!!
  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
  • MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
  • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component@Bean注入
  • 要想根据注解FieldFill.xxx字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
  • 不需要根据任何来区分可以使用父类的fillStrategy方法
  1. public enum FieldFill {
  2. /**
  3. * 默认不处理
  4. */
  5. DEFAULT,
  6. /**
  7. * 插入填充字段
  8. */
  9. INSERT,
  10. /**
  11. * 更新填充字段
  12. */
  13. UPDATE,
  14. /**
  15. * 插入和更新填充字段
  16. */
  17. INSERT_UPDATE
  18. }

乐观锁插件

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

配置乐观锁插件

  1. @Bean
  2. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
  3. return new OptimisticLockerInterceptor();
  4. }

添加注解

实体类和数据表都要加上version字段

在实体类的version字段上加上注解

@Version

  1. @Version
  2. private Integer version;

特别说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • **update(entity, wrapper)** 方法下, **wrapper** 不能复用

测试乐观锁

注意:

测试乐观锁的时候必须先查询再修改

  1. @Test
  2. public void update() {
  3. //先查询
  4. User user = userMapper.selectById(1315944359069253633L);
  5. user.setAge(25);
  6. user.setEmail("lemonxe@qq.com");
  7. //在修改
  8. int update = userMapper.updateById(user);
  9. System.out.println("成功修改:" + update);
  10. }

分页插件

配置分页插件

  1. @Bean
  2. public PaginationInterceptor paginationInterceptor() {
  3. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  4. // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
  5. // paginationInterceptor.setOverflow(false);
  6. // 设置最大单页限制数量,默认 500 条,-1 不受限制
  7. // paginationInterceptor.setLimit(500);
  8. // 开启 count 的 join 优化,只针对部分 left join
  9. paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
  10. return paginationInterceptor;
  11. }

逻辑删除

说明:

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

字段类型支持说明:

  • 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
  • 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

附录:

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
  • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

添加逻辑删除字段

表中添加逻辑删除字段 , 对应实体类添加属性

添加注解

在实体类的逻辑删除属性上添加注解

@TableLogic

  1. @TableLogic
  2. private Integer deleted;

配置逻辑删除插件

application.yml

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以不用再字段上加注解@TableLogic)
  5. logic-delete-value: 1 # 逻辑已删除值(默认为 1)
  6. logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

常见问题

如何 insert ?

  1. 字段在数据库定义默认值(推荐)
  2. insert 前自己 set 值
  3. 使用自动填充功能

删除接口自动填充功能失效

  1. 使用 update 方法并: UpdateWrapper.set(column, value)(推荐)
  2. 使用 update 方法并: UpdateWrapper.setSql("column=value")
  3. 使用Sql注入器注入LogicDeleteByIdWithFill并使用(推荐)

条件构造器

条件构造器官方文档

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

代码生成器依赖

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-generator</artifactId>
  4. <version>3.4.0</version>
  5. </dependency>

模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl

  1. <dependency>
  2. <groupId>org.apache.velocity</groupId>
  3. <artifactId>velocity-engine-core</artifactId>
  4. <version>2.2</version>
  5. </dependency>

示例代码

  1. //代码生成器
  2. @Test
  3. public void run() {
  4. AutoGenerator mpg = new AutoGenerator();
  5. // 全局配置
  6. GlobalConfig gc = new GlobalConfig();
  7. String projectPath = System.getProperty("user.dir");
  8. gc.setOutputDir(projectPath + "/src/main/java");
  9. gc.setAuthor("xiejinchi");
  10. gc.setOpen(false); //生成后是否打开资源管理器
  11. gc.setFileOverride(false); //重新生成时是否覆盖
  12. gc.setServiceName("%sService"); //去掉service接口的首字母I
  13. gc.setIdType(IdType.ID_WORKER); //主键策略
  14. gc.setDateType(DateType.ONLY_DATE); //定义生成的实体类中日期类型
  15. gc.setSwagger2(true); //开启 Swagger2 模式
  16. mpg.setGlobalConfig(gc);
  17. // 数据源配置
  18. DataSourceConfig dsc = new DataSourceConfig();
  19. dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8");
  20. dsc.setDriverName("com.mysql.cj.jdbc.Driver");
  21. dsc.setUsername("root");
  22. dsc.setPassword("garcon");
  23. mpg.setDataSource(dsc);
  24. // 包配置
  25. PackageConfig pc = new PackageConfig();
  26. pc.setParent("com.example.demo");
  27. pc.setModuleName("user"); //模块名
  28. pc.setEntity("entity");
  29. pc.setMapper("mapper");
  30. pc.setService("service");
  31. pc.setController("controller");
  32. mpg.setPackageInfo(pc);
  33. // 策略配置
  34. StrategyConfig strategy = new StrategyConfig();
  35. //表名,多个英文逗号分割
  36. strategy.setInclude("user".split(","));
  37. //数据库映射到实体类的命名策略
  38. strategy.setNaming(NamingStrategy.underline_to_camel);
  39. //数据库表映射到实体类的命名策略
  40. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
  41. //生成实体时去掉表前缀
  42. strategy.setTablePrefix(pc.getModuleName() + "_");
  43. //你自己的父类实体,没有就不用设置!
  44. //strategy.setSuperEntityClass("");
  45. //Lombok模型
  46. strategy.setEntityLombokModel(true);
  47. //reset ful api风格控制器
  48. strategy.setRestControllerStyle(true);
  49. // 你自己的父类控制器,没有就不用设置!
  50. //strategy.setSuperControllerClass("");
  51. //url中驼峰转连接字符
  52. strategy.setControllerMappingHyphenStyle(true);
  53. mpg.setStrategy(strategy);
  54. //执行
  55. mpg.execute();
  56. }