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 操作智能分析阻断,也可自定义拦截规则,预防误操作

    支持数据库

  • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver

  • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

    框架结构

    MybatisPlus3.0教程 - 图1

  • 代码: Gitee | Github

  • 文档:https://mybatis.plus/
  • MyBatis-Plus 入门 - 视频教程 - 慕课网
  • MyBatis-Plus 进阶 - 视频教程 - 慕课网

    快速开始

    我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:

  • 拥有 Java 开发环境以及相应 IDE

  • 熟悉 Spring Boot
  • 熟悉 Maven

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

id name age email
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

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

  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');

Question
如果从零开始用 MyBatis-Plus 来实现该表的增删改查我们需要做什么呢?

初始化工程

创建一个空的 Spring Boot 工程(工程将以 H2 作为默认数据库进行演示)
可以使用 Spring Initializer 快速初始化一个 Spring Boot 工程

添加依赖

引入 Spring Boot Starter 父工程:

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.2.2.RELEASE</version>
  5. <relativePath/>
  6. </parent>

引入 spring-boot-starterspring-boot-starter-testmybatis-plus-boot-starterlombokh2 依赖:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-test</artifactId>
  9. <scope>test</scope>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.projectlombok</groupId>
  13. <artifactId>lombok</artifactId>
  14. <optional>true</optional>
  15. </dependency>
  16. <dependency>
  17. <groupId>com.baomidou</groupId>
  18. <artifactId>mybatis-plus-boot-starter</artifactId>
  19. <version>3.3.0</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>com.h2database</groupId>
  23. <artifactId>h2</artifactId>
  24. <scope>runtime</scope>
  25. </dependency>
  26. </dependencies>

配置

application.yml 配置文件中添加 H2 数据库的相关配置:

  1. # DataSource Config
  2. spring:
  3. datasource:
  4. driver-class-name: org.h2.Driver
  5. schema: classpath:db/schema-h2.sql
  6. data: classpath:db/data-h2.sql
  7. url: jdbc:h2:mem:test
  8. username: root
  9. password: test

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

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件
控制台输出:

  1. User(id=1, name=Jone, age=18, email=test1@baomidou.com)
  2. User(id=2, name=Jack, age=20, email=test2@baomidou.com)
  3. User(id=3, name=Tom, age=28, email=test3@baomidou.com)
  4. User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
  5. User(id=5, name=Billie, age=24, email=test5@baomidou.com)

完整的代码示例请移步:Spring Boot 快速启动示例 | Spring MVC 快速启动示例

小结

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!
从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。
但 MyBatis-Plus 的强大远不止这些功能,想要详细了解 MyBatis-Plus 的强大功能?那就继续往下看吧!

安装

全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调用,所以安装集成 MP3.0 要求如下:

  • JDK 8+
  • Maven or Gradle

JDK7 以及下的请参考 MP2.0 版本,地址:2.0 文档

Release

Spring Boot

Maven:

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-boot-starter</artifactId>
  4. <version>3.3.0</version>
  5. </dependency>

Gradle:

  1. compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.3.0'

Spring MVC

Maven:

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

Gradle:

  1. compile group: 'com.baomidou', name: 'mybatis-plus', version: '3.3.0'

引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。

Snapshot

快照 SNAPSHOT 版本需要添加仓库,且版本号为快照版本 点击查看最新快照版本号
Maven:

  1. <repository>
  2. <id>snapshots</id>
  3. <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
  4. </repository>

Gradle:

  1. repositories {
  2. maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
  3. }

配置

MyBatis-Plus 的配置异常的简单,我们仅需要一些简单的配置即可使用 MyBatis-Plus 的强大功能!
在讲解配置之前,请确保您已经安装了 MyBatis-Plus,如果您尚未安装,请查看 安装 一章。

  • Spring Boot 工程:

    • 配置 MapperScan 注解
      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. }
  • Spring MVC 工程:

    • 配置 MapperScan

      1. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      2. <property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
      3. </bean>
    • 调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory

      1. <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
      2. <property name="dataSource" ref="dataSource"/>
      3. </bean>

      通常来说,一般的简单工程,通过以上配置即可正常使用 MyBatis-Plus,具体可参考以下项目:Spring Boot 快速启动示例Spring MVC 快速启动示例
      同时 MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程,大家可根据自己的项目按需取用

注解

介绍 MybatisPlus 注解包相关类详解(更多详细描述可点击查看源码注释)

注解类包:
👉 mybatis-plus-annotation

@TableName

  • 描述:表名注解 | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | —- | | value | String | 否 | “” | 表名 | | schema | String | 否 | “” | schema | | keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值) | | resultMap | String | 否 | “” | xml 中 resultMap 的 id | | autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入) |

关于autoResultMap的说明:
mp会自动构建一个ResultMap并注入到mybatis里(一般用不上).下面讲两句: 因为mp底层是mybatis,所以一些mybatis的常识你要知道,mp只是帮你注入了常用crud到mybatis里 注入之前可以说是动态的(根据你entity的字段以及注解变化而变化),但是注入之后是静态的(等于你写在xml的东西) 而对于直接指定typeHandler,mybatis只支持你写在2个地方:

  1. 定义在resultMap里,只作用于select查询的返回结果封装
  2. 定义在insertupdatesql的#{property}里的property后面(例:#{property,typehandler=xxx.xxx.xxx}),只作用于设置值 而除了这两种直接指定typeHandler,mybatis有一个全局的扫描你自己的typeHandler包的配置,这是根据你的property的类型去找typeHandler并使用.

    @TableId

  • 描述:主键注解 | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | :—-: | | value | String | 否 | “” | 主键字段名 | | type | Enum | 否 | IdType.NONE | 主键类型 |

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)

@TableField

  • 描述:字段注解(非主键) | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | :—-: | | value | String | 否 | “” | 字段名 | | el | String | 否 | “” | 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分 | | exist | boolean | 否 | true | 是否为数据库表字段 | | condition | String | 否 | “” | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考 | | update | String | 否 | “” | 字段 update set 部分注入, 例如:update=”%s+1”:表示更新时会set version=version+1(该属性优先级高于 el 属性) | | insertStrategy | Enum | N | DEFAULT | 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) | | updateStrategy | Enum | N | DEFAULT | 举例:IGNORED: update table_a set column=#{columnProperty} | | whereStrategy | Enum | N | DEFAULT | 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> | | fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 | | select | boolean | 否 | true | 是否进行 select 查询 | | keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 | | jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC类型 (该默认值不代表会按照该值生效) | | typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) | | numericScale | String | 否 | “” | 指定小数点后保留的位数 |

关于jdbcTypetypeHandler以及numericScale的说明:
numericScale只生效于 update 的sql. jdbcTypetypeHandler如果不配合@TableName#autoResultMap = true一起使用,也只生效于 update 的sql. 对于typeHandler如果你的字段类型和set进去的类型为equals关系,则只需要让你的typeHandler让Mybatis加载到即可,不需要使用注解

FieldStrategy

描述
IGNORED 忽略判断
NOT_NULL 非NULL判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT 追随全局配置

FieldFill

描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段

@Version

  • 描述:乐观锁注解、标记 @Verison 在字段上

    @EnumValue

  • 描述:通枚举类注解(注解在枚举字段上)

    @TableLogic

  • 描述:表字段逻辑处理注解(逻辑删除) | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | :—-: | | value | String | 否 | “” | 逻辑未删除值 | | delval | String | 否 | “” | 逻辑删除值 |

@SqlParser

  • 描述:租户注解,支持method上以及mapper接口上 | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | :—-: | | filter | boolean | 否 | false | true: 表示过滤SQL解析,即不会进入ISqlParser解析链,否则会进解析链并追加例如tenant_id等条件 |

@KeySequence

  • 描述:序列主键策略 oracle
  • 属性:value、resultMap | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | :—-: | :—-: | :—-: | :—-: | :—-: | | value | String | 否 | “” | 序列名 | | clazz | Class | 否 | Long.class | id的类型, 可以指定String.class,这样返回的Sequence值是字符串”1” |

核心功能

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
特别说明:
自定义模板有哪些可用参数?Github Gitee AbstractTemplateEngine 类中方法 getObjectMap 返回 objectMap 的所有值都可用。
演示效果图:
MybatisPlus3.0教程 - 图2

  1. // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
  2. public class CodeGenerator {
  3. /**
  4. * <p>
  5. * 读取控制台内容
  6. * </p>
  7. */
  8. public static String scanner(String tip) {
  9. Scanner scanner = new Scanner(System.in);
  10. StringBuilder help = new StringBuilder();
  11. help.append("请输入" + tip + ":");
  12. System.out.println(help.toString());
  13. if (scanner.hasNext()) {
  14. String ipt = scanner.next();
  15. if (StringUtils.isNotEmpty(ipt)) {
  16. return ipt;
  17. }
  18. }
  19. throw new MybatisPlusException("请输入正确的" + tip + "!");
  20. }
  21. public static void main(String[] args) {
  22. // 代码生成器
  23. AutoGenerator mpg = new AutoGenerator();
  24. // 全局配置
  25. GlobalConfig gc = new GlobalConfig();
  26. String projectPath = System.getProperty("user.dir");
  27. gc.setOutputDir(projectPath + "/src/main/java");
  28. gc.setAuthor("jobob");
  29. gc.setOpen(false);
  30. // gc.setSwagger2(true); 实体属性 Swagger2 注解
  31. mpg.setGlobalConfig(gc);
  32. // 数据源配置
  33. DataSourceConfig dsc = new DataSourceConfig();
  34. dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
  35. // dsc.setSchemaName("public");
  36. dsc.setDriverName("com.mysql.jdbc.Driver");
  37. dsc.setUsername("root");
  38. dsc.setPassword("密码");
  39. mpg.setDataSource(dsc);
  40. // 包配置
  41. PackageConfig pc = new PackageConfig();
  42. pc.setModuleName(scanner("模块名"));
  43. pc.setParent("com.baomidou.ant");
  44. mpg.setPackageInfo(pc);
  45. // 自定义配置
  46. InjectionConfig cfg = new InjectionConfig() {
  47. @Override
  48. public void initMap() {
  49. // to do nothing
  50. }
  51. };
  52. // 如果模板引擎是 freemarker
  53. String templatePath = "/templates/mapper.xml.ftl";
  54. // 如果模板引擎是 velocity
  55. // String templatePath = "/templates/mapper.xml.vm";
  56. // 自定义输出配置
  57. List<FileOutConfig> focList = new ArrayList<>();
  58. // 自定义配置会被优先输出
  59. focList.add(new FileOutConfig(templatePath) {
  60. @Override
  61. public String outputFile(TableInfo tableInfo) {
  62. // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
  63. return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
  64. + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
  65. }
  66. });
  67. /*
  68. cfg.setFileCreate(new IFileCreate() {
  69. @Override
  70. public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
  71. // 判断自定义文件夹是否需要创建
  72. checkDir("调用默认方法创建的目录");
  73. return false;
  74. }
  75. });
  76. */
  77. cfg.setFileOutConfigList(focList);
  78. mpg.setCfg(cfg);
  79. // 配置模板
  80. TemplateConfig templateConfig = new TemplateConfig();
  81. // 配置自定义输出模板
  82. //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
  83. // templateConfig.setEntity("templates/entity2.java");
  84. // templateConfig.setService();
  85. // templateConfig.setController();
  86. templateConfig.setXml(null);
  87. mpg.setTemplate(templateConfig);
  88. // 策略配置
  89. StrategyConfig strategy = new StrategyConfig();
  90. strategy.setNaming(NamingStrategy.underline_to_camel);
  91. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
  92. strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
  93. strategy.setEntityLombokModel(true);
  94. strategy.setRestControllerStyle(true);
  95. // 公共父类
  96. strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
  97. // 写于父类中的公共字段
  98. strategy.setSuperEntityColumns("id");
  99. strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
  100. strategy.setControllerMappingHyphenStyle(true);
  101. strategy.setTablePrefix(pc.getModuleName() + "_");
  102. mpg.setStrategy(strategy);
  103. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
  104. mpg.execute();
  105. }
  106. }

使用教程

添加依赖

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

  • 添加 代码生成器 依赖

    1. <dependency>
    2. <groupId>com.baomidou</groupId>
    3. <artifactId>mybatis-plus-generator</artifactId>
    4. <version>3.3.0</version>
    5. </dependency>
  • 添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。
    Velocity(默认):

    1. <dependency>
    2. <groupId>org.apache.velocity</groupId>
    3. <artifactId>velocity-engine-core</artifactId>
    4. <version>2.1</version>
    5. </dependency>
  • Freemarker:

    1. <dependency>
    2. <groupId>org.freemarker</groupId>
    3. <artifactId>freemarker</artifactId>
    4. <version>2.3.29</version>
    5. </dependency>
  • Beetl:

    1. <dependency>
    2. <groupId>com.ibeetl</groupId>
    3. <artifactId>beetl</artifactId>
    4. <version>3.0.16.RELEASE</version>
    5. </dependency>
  • 注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

    1. AutoGenerator generator = new AutoGenerator();
    2. // set freemarker engine
    3. generator.setTemplateEngine(new FreemarkerTemplateEngine());
    4. // set beetl engine
    5. generator.setTemplateEngine(new BeetlTemplateEngine());
    6. // set custom engine (reference class is your custom engine class)
    7. generator.setTemplateEngine(new CustomTemplateEngine());
    8. // other config
    9. ...

    编写配置

    MyBatis-Plus 的代码生成器提供了大量的自定义参数供用户选择,能够满足绝大部分人的使用需求。

  • 配置 GlobalConfig

    1. GlobalConfig globalConfig = new GlobalConfig();
    2. globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
    3. globalConfig.setAuthor("jobob");
    4. globalConfig.setOpen(false);
  • 配置 DataSourceConfig

    1. DataSourceConfig dataSourceConfig = new DataSourceConfig();
    2. dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
    3. dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
    4. dataSourceConfig.setUsername("root");
    5. dataSourceConfig.setPassword("password");

    自定义模板引擎

    请继承类 com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine

    自定义代码模板

    1. //指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
    2. //注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别
    3. TemplateConfig templateConfig = new TemplateConfig()
    4. .setEntity("templates/entity2.java");
    5. AutoGenerator mpg = new AutoGenerator();
    6. //配置自定义模板
    7. mpg.setTemplate(templateConfig);

    自定义属性注入

    1. InjectionConfig injectionConfig = new InjectionConfig() {
    2. //自定义属性注入:abc
    3. //在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
    4. @Override
    5. public void initMap() {
    6. Map<String, Object> map = new HashMap<>();
    7. map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
    8. this.setMap(map);
    9. }
    10. };
    11. AutoGenerator mpg = new AutoGenerator();
    12. //配置自定义属性注入
    13. mpg.setCfg(injectionConfig);
    1. entity2.java.ftl
    2. 自定义属性注入abc=${cfg.abc}
    3. entity2.java.vm
    4. 自定义属性注入abc=$!{cfg.abc}

    字段其他信息查询注入

    MybatisPlus3.0教程 - 图3

    1. new DataSourceConfig().setDbQuery(new MySqlQuery() {
    2. /**
    3. * 重写父类预留查询自定义字段<br>
    4. * 这里查询的 SQL 对应父类 tableFieldsSql 的查询字段,默认不能满足你的需求请重写它<br>
    5. * 模板中调用: table.fields 获取所有字段信息,
    6. * 然后循环字段获取 field.customMap 从 MAP 中获取注入字段如下 NULL 或者 PRIVILEGES
    7. */
    8. @Override
    9. public String[] fieldCustom() {
    10. return new String[]{"NULL", "PRIVILEGES"};
    11. }
    12. })

    CRUD 接口

    Service CRUD 接口

    说明:

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,

  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper条件构造器

    Save

    1. // 插入一条记录(选择字段,策略插入)
    2. boolean save(T entity);
    3. // 插入(批量)
    4. boolean saveBatch(Collection<T> entityList);
    5. // 插入(批量)
    6. boolean saveBatch(Collection<T> entityList, int batchSize);
    参数说明
    | 类型 | 参数名 | 描述 | | :—-: | :—-: | :—-: | | T | entity | 实体对象 | | Collection | entityList | 实体对象集合 | | int | batchSize | 插入批次数量 |

SaveOrUpdate

  1. // TableId 注解存在更新记录,否插入一条记录
  2. boolean saveOrUpdate(T entity);
  3. // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
  4. boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
  5. // 批量修改插入
  6. boolean saveOrUpdateBatch(Collection<T> entityList);
  7. // 批量修改插入
  8. boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

参数说明
类型 参数名 描述
T entity 实体对象
Wrapper updateWrapper 实体对象封装操作类 UpdateWrapper
Collection entityList 实体对象集合
int batchSize 插入批次数量

Remove

  1. // 根据 entity 条件,删除记录
  2. boolean remove(Wrapper<T> queryWrapper);
  3. // 根据 ID 删除
  4. boolean removeById(Serializable id);
  5. // 根据 columnMap 条件,删除记录
  6. boolean removeByMap(Map<String, Object> columnMap);
  7. // 删除(根据ID 批量删除)
  8. boolean removeByIds(Collection<? extends Serializable> idList);

参数说明
类型 参数名 描述
Wrapper queryWrapper 实体包装类 QueryWrapper
Serializable id 主键ID
Map columnMap 表字段 map 对象
Collection<? extends Serializable> idList 主键ID列表

Update

  1. // 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
  2. boolean update(Wrapper<T> updateWrapper);
  3. // 根据 whereEntity 条件,更新记录
  4. boolean update(T entity, Wrapper<T> updateWrapper);
  5. // 根据 ID 选择修改
  6. boolean updateById(T entity);
  7. // 根据ID 批量更新
  8. boolean updateBatchById(Collection<T> entityList);
  9. // 根据ID 批量更新
  10. boolean updateBatchById(Collection<T> entityList, int batchSize);

参数说明
类型 参数名 描述
Wrapper updateWrapper 实体对象封装操作类 UpdateWrapper
T entity 实体对象
Collection entityList 实体对象集合
int batchSize 更新批次数量

Get

  1. // 根据 ID 查询
  2. T getById(Serializable id);
  3. // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
  4. T getOne(Wrapper<T> queryWrapper);
  5. // 根据 Wrapper,查询一条记录
  6. T getOne(Wrapper<T> queryWrapper, boolean throwEx);
  7. // 根据 Wrapper,查询一条记录
  8. Map<String, Object> getMap(Wrapper<T> queryWrapper);
  9. // 根据 Wrapper,查询一条记录
  10. <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

参数说明
类型 参数名 描述
Serializable id 主键ID
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper
boolean throwEx 有多个 result 是否抛出异常
T entity 实体对象
Function<? super Object, V> mapper 转换函数

List

  1. // 查询所有
  2. List<T> list();
  3. // 查询列表
  4. List<T> list(Wrapper<T> queryWrapper);
  5. // 查询(根据ID 批量查询)
  6. Collection<T> listByIds(Collection<? extends Serializable> idList);
  7. // 查询(根据 columnMap 条件)
  8. Collection<T> listByMap(Map<String, Object> columnMap);
  9. // 查询所有列表
  10. List<Map<String, Object>> listMaps();
  11. // 查询列表
  12. List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
  13. // 查询全部记录
  14. List<Object> listObjs();
  15. // 查询全部记录
  16. <V> List<V> listObjs(Function<? super Object, V> mapper);
  17. // 根据 Wrapper 条件,查询全部记录
  18. List<Object> listObjs(Wrapper<T> queryWrapper);
  19. // 根据 Wrapper 条件,查询全部记录
  20. <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

参数说明
类型 参数名 描述
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper
Collection<? extends Serializable> idList 主键ID列表
Map<?String, Object> columnMap 表字段 map 对象
Function<? super Object, V> mapper 转换函数

Page

  1. // 无条件翻页查询
  2. IPage<T> page(IPage<T> page);
  3. // 翻页查询
  4. IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
  5. // 无条件翻页查询
  6. IPage<Map<String, Object>> pageMaps(IPage<T> page);
  7. // 翻页查询
  8. IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

参数说明
类型 参数名 描述
IPage page 翻页对象
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper

Count

  1. // 查询总记录数
  2. int count();
  3. // 根据 Wrapper 条件,查询总记录数
  4. int count(Wrapper<T> queryWrapper);

参数说明
类型 参数名 描述
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper

Chain

query

  1. // 链式查询 普通
  2. QueryChainWrapper<T> query();
  3. // 链式查询 lambda 式。注意:不支持 Kotlin
  4. LambdaQueryChainWrapper<T> lambdaQuery();
  5. // 示例:
  6. query().eq("column", value).one();
  7. lambdaQuery().eq(Entity::getId, value).list();

update

  1. // 链式更改 普通
  2. UpdateChainWrapper<T> update();
  3. // 链式更改 lambda 式。注意:不支持 Kotlin
  4. LambdaUpdateChainWrapper<T> lambdaUpdate();
  5. // 示例:
  6. update().eq("column", value).remove();
  7. lambdaUpdate().eq(Entity::getId, value).update(entity);

Mapper CRUD 接口

说明:

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

    Insert

    1. // 插入一条记录
    2. int insert(T entity);
    参数说明
    | 类型 | 参数名 | 描述 | | :—-: | :—-: | :—-: | | T | entity | 实体对象 |

Delete

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

参数说明
类型 参数名 描述
Wrapper wrapper 实体对象封装操作类(可以为 null)
Collection<? extends Serializable> idList 主键ID列表(不能为 null 以及 empty)
Serializable id 主键ID
Map columnMap 表字段 map 对象

Update

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

参数说明
类型 参数名 描述
T entity 实体对象 (set 条件值,可为 null)
Wrapper updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)

Select

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

参数说明
类型 参数名 描述
Serializable id 主键ID
Wrapper queryWrapper 实体对象封装操作类(可以为 null)
Collection<? extends Serializable> idList 主键ID列表(不能为 null 以及 empty)
Map columnMap 表字段 map 对象
IPage page 分页查询条件(可以为 RowBounds.DEFAULT)

mapper 层 选装件

说明:
选装件位于 com.baomidou.mybatisplus.extension.injector.methods.additional 包下 需要配合Sql 注入器使用案例
使用详细见源码注释

AlwaysUpdateSomeColumnById

  1. int alwaysUpdateSomeColumnById(T entity);

insertBatchSomeColumn

  1. int insertBatchSomeColumn(List<T> entityList);

deleteByIdWithFill

  1. int deleteByIdWithFill(T entity);

条件构造器

说明:

  • 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中
  • 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true
  • 以下出现的泛型Param均为Wrapper的子类实例(均具有AbstractWrapper的所有方法)
  • 以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是函数(例:Entity::getId,Entity为实体类,getId为字段idgetMethod)
  • 以下方法入参中的R column均表示数据库字段,当R具体类型为String时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!,另当R具体类型为SFunction时项目runtime不支持eclipse自家的编译器!!!
  • 以下举例均为使用普通wrapper,入参为MapList的均以json形式表现!
  • 使用中如果入参的Map或者List,则不会加入最后生成的sql中!!!
  • 有任何疑问就点开源码看,看不懂函数点击我学习新知识

警告:
不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输

  1. wrapper 很重
  2. 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
  3. 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
  4. 我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr

    AbstractWrapper

    说明:
    QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
    用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
    注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

    allEq

  1. allEq(Map<R, V> params)
  2. allEq(Map<R, V> params, boolean null2IsNull)
  3. allEq(boolean condition, Map<R, V> params, boolean null2IsNull)

个别参数说明:
params : key为数据库字段名,value为字段值
null2IsNull : 为true则在mapvaluenull时调用 isNull 方法,为false时则忽略valuenull

  • 例1: allEq({id:1,name:"老王",age:null})—->id = 1 and name = '老王' and age is null
  • 例2: allEq({id:1,name:"老王",age:null}, false)—->id = 1 and name = '老王'
  1. allEq(BiPredicate<R, V> filter, Map<R, V> params)
  2. allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
  3. allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)

个别参数说明:
filter : 过滤函数,是否允许字段传入比对条件中
paramsnull2IsNull : 同上

  • 例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})—->name = '老王' and age is null
  • 例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)—->name = '老王'

    eq

  1. eq(R column, Object val)
  2. eq(boolean condition, R column, Object val)
  • 等于 =
  • 例: eq("name", "老王")—->name = '老王'

    ne

    1. ne(R column, Object val)
    2. ne(boolean condition, R column, Object val)
  • 不等于 <>

  • 例: ne("name", "老王")—->name <> '老王'

    gt

    1. gt(R column, Object val)
    2. gt(boolean condition, R column, Object val)
  • 大于 >

  • 例: gt("age", 18)—->age > 18

    ge

  1. ge(R column, Object val)
  2. ge(boolean condition, R column, Object val)
  • 大于等于 >=
  • 例: ge("age", 18)—->age >= 18

    lt

  1. lt(R column, Object val)
  2. lt(boolean condition, R column, Object val)
  • 小于 <
  • 例: lt("age", 18)—->age < 18

    le

  1. le(R column, Object val)
  2. le(boolean condition, R column, Object val)
  • 小于等于 <=
  • 例: le("age", 18)—->age <= 18

    between

  1. between(R column, Object val1, Object val2)
  2. between(boolean condition, R column, Object val1, Object val2)
  • BETWEEN 值1 AND 值2
  • 例: between("age", 18, 30)—->age between 18 and 30

    notBetween

  1. notBetween(R column, Object val1, Object val2)
  2. notBetween(boolean condition, R column, Object val1, Object val2)
  • NOT BETWEEN 值1 AND 值2
  • 例: notBetween("age", 18, 30)—->age not between 18 and 30

    like

  1. like(R column, Object val)
  2. like(boolean condition, R column, Object val)
  • LIKE ‘%值%’
  • 例: like("name", "王")—->name like '%王%'

    notLike

  1. notLike(R column, Object val)
  2. notLike(boolean condition, R column, Object val)
  • NOT LIKE ‘%值%’
  • 例: notLike("name", "王")—->name not like '%王%'

    likeLeft

  1. likeLeft(R column, Object val)
  2. likeLeft(boolean condition, R column, Object val)
  • LIKE ‘%值’
  • 例: likeLeft("name", "王")—->name like '%王'

    likeRight

  1. likeRight(R column, Object val)
  2. likeRight(boolean condition, R column, Object val)
  • LIKE ‘值%’
  • 例: likeRight("name", "王")—->name like '王%'

    isNull

  1. isNull(R column)
  2. isNull(boolean condition, R column)
  • 字段 IS NULL
  • 例: isNull("name")—->name is null

    isNotNull

  1. isNotNull(R column)
  2. isNotNull(boolean condition, R column)
  • 字段 IS NOT NULL
  • 例: isNotNull("name")—->name is not null

    in

  1. in(R column, Collection<?> value)
  2. in(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)
  • 例: in("age",{1,2,3})—->age in (1,2,3)
  1. in(R column, Object... values)
  2. in(boolean condition, R column, Object... values)
  • 字段 IN (v0, v1, …)
  • 例: in("age", 1, 2, 3)—->age in (1,2,3)

    notIn

  1. notIn(R column, Collection<?> value)
  2. notIn(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)
  • 例: notIn("age",{1,2,3})—->age not in (1,2,3)
  1. notIn(R column, Object... values)
  2. notIn(boolean condition, R column, Object... values)
  • 字段 NOT IN (v0, v1, …)
  • 例: notIn("age", 1, 2, 3)—->age not in (1,2,3)

    inSql

  1. inSql(R column, String inValue)
  2. inSql(boolean condition, R column, String inValue)
  • 字段 IN ( sql语句 )
  • 例: inSql("age", "1,2,3,4,5,6")—->age in (1,2,3,4,5,6)
  • 例: inSql("id", "select id from table where id < 3")—->id in (select id from table where id < 3)

    notInSql

  1. notInSql(R column, String inValue)
  2. notInSql(boolean condition, R column, String inValue)
  • 字段 NOT IN ( sql语句 )
  • 例: notInSql("age", "1,2,3,4,5,6")—->age not in (1,2,3,4,5,6)
  • 例: notInSql("id", "select id from table where id < 3")—->age not in (select id from table where id < 3)

    groupBy

  1. groupBy(R... columns)
  2. groupBy(boolean condition, R... columns)
  • 分组:GROUP BY 字段, …
  • 例: groupBy("id", "name")—->group by id,name

    orderByAsc

  1. orderByAsc(R... columns)
  2. orderByAsc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … ASC
  • 例: orderByAsc("id", "name")—->order by id ASC,name ASC

    orderByDesc

  1. orderByDesc(R... columns)
  2. orderByDesc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … DESC
  • 例: orderByDesc("id", "name")—->order by id DESC,name DESC

    orderBy

    1. orderBy(boolean condition, boolean isAsc, R... columns)
  • 排序:ORDER BY 字段, …

  • 例: orderBy(true, true, "id", "name")—->order by id ASC,name ASC

    having

  1. having(String sqlHaving, Object... params)
  2. having(boolean condition, String sqlHaving, Object... params)
  • HAVING ( sql语句 )
  • 例: having("sum(age) > 10")—->having sum(age) > 10
  • 例: having("sum(age) > {0}", 11)—->having sum(age) > 11

    or

  1. or()
  2. or(boolean condition)
  • 拼接 OR

注意事项:
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

  • 例: eq("id",1).or().eq("name","老王")—->id = 1 or name = '老王'
  1. or(Consumer<Param> consumer)
  2. or(boolean condition, Consumer<Param> consumer)
  • OR 嵌套
  • 例: or(i -> i.eq("name", "李白").ne("status", "活着"))—->or (name = '李白' and status <> '活着')

    and

  1. and(Consumer<Param> consumer)
  2. and(boolean condition, Consumer<Param> consumer)
  • AND 嵌套
  • 例: and(i -> i.eq("name", "李白").ne("status", "活着"))—->and (name = '李白' and status <> '活着')

    nested

  1. nested(Consumer<Param> consumer)
  2. nested(boolean condition, Consumer<Param> consumer)
  • 正常嵌套 不带 AND 或者 OR
  • 例: nested(i -> i.eq("name", "李白").ne("status", "活着"))—->(name = '李白' and status <> '活着')

    apply

  1. apply(String applySql, Object... params)
  2. apply(boolean condition, String applySql, Object... params)
  • 拼接 sql

注意事项:
该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")—->id = 1
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")—->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")—->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

    last

  1. last(String lastSql)
  2. last(boolean condition, String lastSql)
  • 无视优化规则直接拼接到 sql 的最后

注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

  • 例: last("limit 1")

    exists

  1. exists(String existsSql)
  2. exists(boolean condition, String existsSql)
  • 拼接 EXISTS ( sql语句 )
  • 例: exists("select id from table where age = 1")—->exists (select id from table where age = 1)

    notExists

  1. notExists(String notExistsSql)
  2. notExists(boolean condition, String notExistsSql)
  • 拼接 NOT EXISTS ( sql语句 )
  • 例: notExists("select id from table where age = 1")—->not exists (select id from table where age = 1)

    QueryWrapper

    说明:
    继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
    及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

    select

  1. select(String... sqlSelect)
  2. select(Predicate<TableFieldInfo> predicate)
  3. select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
  • 设置查询字段

说明:
以上方分法为两类.
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准

  • 例: select("id", "name", "age")
  • 例: select(i -> i.getProperty().startsWith("test"))

    excludeColumns @Deprecated

  • 排除查询字段

已从3.0.5版本上移除此方法!

UpdateWrapper

说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

  1. set(String column, Object val)
  2. set(boolean condition, String column, Object val)
  • SQL SET 字段
  • 例: set("name", "老李头")
  • 例: set("name", "")—->数据库字段值变为空字符串
  • 例: set("name", null)—->数据库字段值变为null

    setSql

    1. setSql(String sql)
  • 设置 SET 部分 SQL

  • 例: setSql("name = '老李头'")

    lambda

  • 获取 LambdaWrapper
    QueryWrapper中是获取LambdaQueryWrapper
    UpdateWrapper中是获取LambdaUpdateWrapper

    使用 Wrapper 自定义SQL

    需求来源:
    在使用了mybatis-plus之后, 自定义SQL的同时也想使用Wrapper的便利应该怎么办? 在mybatis-plus版本3.0.7得到了完美解决 版本需要大于或等于3.0.7, 以下两种方案取其一即可

    Service.java

    1. mysqlMapper.getAll(Wrappers.<MysqlData>lambdaQuery().eq(MysqlData::getGroup, 1));

    方案一 注解方式 Mapper.java

    1. @Select("select * from mysql_data ${ew.customSqlSegment}")
    2. List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

    方案二 XML形式 Mapper.xml

    1. <select id="getAll" resultType="MysqlData">
    2. SELECT * FROM mysql_data ${ew.customSqlSegment}
    3. </select>

    分页插件

    示例工程:
    👉 mybatis-plus-sample-pagination

    1. <!-- spring xml 方式 -->
    2. <property name="plugins">
    3. <array>
    4. <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
    5. <property name="sqlParser" ref="自定义解析类、可以没有"/>
    6. <property name="dialectClazz" value="自定义方言类、可以没有"/>
    7. <!-- COUNT SQL 解析.可以没有 -->
    8. <property name="countSqlParser" ref="countSqlParser"/>
    9. </bean>
    10. </array>
    11. </property>
    12. <bean id="countSqlParser" class="com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize">
    13. <!-- 设置为 true 可以优化部分 left join 的sql -->
    14. <property name="optimizeJoin" value="true"/>
    15. </bean>
    1. //Spring boot方式
    2. @EnableTransactionManagement
    3. @Configuration
    4. @MapperScan("com.baomidou.cloud.service.*.mapper*")
    5. public class MybatisPlusConfig {
    6. @Bean
    7. public PaginationInterceptor paginationInterceptor() {
    8. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    9. // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
    10. // paginationInterceptor.setOverflow(false);
    11. // 设置最大单页限制数量,默认 500 条,-1 不受限制
    12. // paginationInterceptor.setLimit(500);
    13. // 开启 count 的 join 优化,只针对部分 left join
    14. paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
    15. return paginationInterceptor;
    16. }
    17. }

    XML 自定义分页

  • UserMapper.java 方法内容

    1. public interface UserMapper {//可以继承或者不继承BaseMapper
    2. /**
    3. * <p>
    4. * 查询 : 根据state状态查询用户列表,分页显示
    5. * </p>
    6. *
    7. * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
    8. * @param state 状态
    9. * @return 分页对象
    10. */
    11. IPage<User> selectPageVo(Page<?> page, Integer state);
    12. }
  • UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页

    1. <select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
    2. SELECT id,name FROM user WHERE state=#{state}
    3. </select>
  • UserServiceImpl.java 调用分页方法

    1. public IPage<User> selectUserPage(Page<User> page, Integer state) {
    2. // 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分
    3. // page.setOptimizeCountSql(false);
    4. // 当 total 为小于 0 或者设置 setSearchCount(false) 分页插件不会进行 count 查询
    5. // 要点!! 分页返回的对象与传入的对象是同一个
    6. return userMapper.selectPageVo(page, state);
    7. }

    Sequence主键

    主键生成策略必须使用INPUT
    支持父类定义@KeySequence子类继承使用
    支持主键类型指定(3.3.0开始自动识别主键类型)
    内置支持:

  • DB2KeyGenerator

  • H2KeyGenerator
  • KingbaseKeyGenerator
  • OracleKeyGenerator
  • PostgreKeyGenerator

如果内置支持不满足你的需求,可实现IKeyGenerator接口来进行扩展.
举个栗子

  1. @KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
  2. public class YourEntity {
  3. @TableId(value = "ID_STR", type = IdType.INPUT)
  4. private String idStr;
  5. }

Spring-Boot

方式一:使用配置类

  1. @Bean
  2. public IKeyGenerator keyGenerator() {
  3. return new H2KeyGenerator();
  4. }

方式二:通过MybatisPlusPropertiesCustomizer自定义

  1. @Bean
  2. public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
  3. return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator());
  4. }

Spring

方式一: XML配置

  1. <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
  2. <property name="dbConfig" ref="dbConfig"/>
  3. </bean>
  4. <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
  5. <property name="keyGenerator" ref="keyGenerator"/>
  6. </bean>
  7. <bean id="keyGenerator" class="com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator"/>

方式二:注解配置

  1. @Bean
  2. public GlobalConfig globalConfig() {
  3. GlobalConfig conf = new GlobalConfig();
  4. conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerator(new H2KeyGenerator()));
  5. return conf;
  6. }

自定义ID生成器

自3.3.0开始,默认使用雪花算法+UUID(不含中划线)
自定义示例工程:

  • spring-boot示例 :传送门 | 方法 | 主键生成策略 | 主键类型 | 说明 | | —- | —- | —- | —- | | nextId | ASSIGN_ID,ID_WORKERID_WORKER_STR | Long,Integer,String | 支持自动转换为String类型,但数值类型不支持自动转换,需精准匹配,例如返回Long,实体主键就不支持定义为Integer | | nextUUID | ASSIGN_UUID,UUID | String | 默认不含中划线的UUID生成 |

Spring-Boot

方式一:声明为Bean供Spring扫描注入

  1. @Component
  2. public class CustomIdGenerator implements IdentifierGenerator {
  3. @Override
  4. public Long nextId(Object entity) {
  5. //可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成.
  6. String bizKey = entity.getClass().getName();
  7. //根据bizKey调用分布式ID生成
  8. long id = ....;
  9. //返回生成的id值即可.
  10. return id;
  11. }
  12. }

方式二:使用配置类

  1. @Bean
  2. public IdentifierGenerator idGenerator() {
  3. return new CustomIdGenerator();
  4. }

方式三:通过MybatisPlusPropertiesCustomizer自定义

  1. @Bean
  2. public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
  3. return plusProperties -> plusProperties.getGlobalConfig().setIdentifierGenerator(new CustomIdGenerator());
  4. }

Spring

方式一: XML配置

  1. <bean name="customIdGenerator" class="com.baomidou.samples.incrementer.CustomIdGenerator"/>
  2. <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
  3. <property name="identifierGenerator" ref="customIdGenerator"/>
  4. </bean>

方式二:注解配置

  1. @Bean
  2. public GlobalConfig globalConfig() {
  3. GlobalConfig conf = new GlobalConfig();
  4. conf.setIdentifierGenerator(new CustomIdGenerator());
  5. return conf;
  6. }

插件扩展

热加载

3.0.6版本上移除了该功能,不过最新快照版已加回来并打上废弃标识,3.1.0版本上已完全移除

开启动态加载 mapper.xml

  • 多数据源配置多个 MybatisMapperRefresh 启动 bean
  • 默认情况下,eclipse保存会自动编译,idea需自己手动编译一次

    1. 参数说明:
    2. sqlSessionFactory:session工厂
    3. mapperLocations:mapper匹配路径
    4. enabled:是否开启动态加载 默认:false
    5. delaySeconds:项目启动延迟加载时间 单位:秒 默认:10s
    6. sleepSeconds:刷新时间间隔 单位:秒 默认:20s
    7. 提供了两个构造,挑选一个配置进入spring配置文件即可:
    8. 构造1:
    9. <bean class="com.baomidou.mybatisplus.spring.MybatisMapperRefresh">
    10. <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    11. <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
    12. <constructor-arg name="enabled" value="true"/>
    13. </bean>
    14. 构造2:
    15. <bean class="com.baomidou.mybatisplus.spring.MybatisMapperRefresh">
    16. <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    17. <constructor-arg name="mapperLocations" value="classpath*:mybatis/mappers/*/*.xml"/>
    18. <constructor-arg name="delaySeconds" value="10"/>
    19. <constructor-arg name="sleepSeconds" value="20"/>
    20. <constructor-arg name="enabled" value="true"/>
    21. </bean>

    逻辑删除

    SpringBoot 配置方式:

  • application.yml 加入配置(如果你的默认值和mp默认的一样,该配置可无):

    1. mybatis-plus:
    2. global-config:
    3. db-config:
    4. logic-delete-field: flag #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
    5. logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    6. logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  • 注册 Bean(3.1.1开始不再需要这一步):

    1. import com.baomidou.mybatisplus.core.injector.ISqlInjector;
    2. import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
    3. import org.springframework.context.annotation.Bean;
    4. import org.springframework.context.annotation.Configuration;
    5. @Configuration
    6. public class MyBatisPlusConfiguration {
    7. @Bean
    8. public ISqlInjector sqlInjector() {
    9. return new LogicSqlInjector();
    10. }
    11. }
  • 实体类字段上加上@TableLogic注解

    1. @TableLogic
    2. private Integer deleted;
  • 效果: 使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)

    1. example
    2. 删除时 update user set deleted=1 where id =1 and deleted=0
    3. 查找时 select * from user where deleted=0
  • 全局逻辑删除: 3.3.0开始支持
    如果公司代码比较规范,比如统一了全局都是flag为逻辑删除字段。
    使用此配置则不需要在实体类上添加 @TableLogic。
    但如果实体类上有 @TableLogic 则以实体上的为准,忽略全局。 即先查找注解再查找全局,都没有则此表没有逻辑删除。

    1. mybatis-plus:
    2. global-config:
    3. db-config:
    4. logic-delete-field: flag #全局逻辑删除字段值

    附件说明

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。

  • 如果你需要再查出来就不应使用逻辑删除,而是以一个状态去表示。

如: 员工离职,账号被锁定等都应该是一个状态字段,此种场景不应使用逻辑删除。

  • 若确需查找删除数据,如老板需要查看历史所有数据的统计汇总信息,请单独手写sql。

    通用枚举

    解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!

    3.1.0开始,如果你无需使用原生枚举,可配置默认枚举来省略扫描通用枚举配置 默认枚举配置

    • 升级说明:
      3.1.0 以下版本改变了原生默认行为,升级时请将默认枚举设置为EnumOrdinalTypeHandler
    • 影响用户:
      实体中使用原生枚举
    • 其他说明:
      配置枚举包扫描的时候能提前注册使用注解枚举的缓存

1、声明通用枚举属性

方式一: 使用 @EnumValue 注解枚举属性 完整示例

  1. public enum GradeEnum {
  2. PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
  3. GradeEnum(int code, String descp) {
  4. this.code = code;
  5. this.descp = descp;
  6. }
  7. @EnumValue//标记数据库存的值是code
  8. private final int code;
  9. //。。。
  10. }

方式二: 枚举属性,实现 IEnum 接口如下:

  1. public enum AgeEnum implements IEnum<Integer> {
  2. ONE(1, "一岁"),
  3. TWO(2, "二岁"),
  4. THREE(3, "三岁");
  5. private int value;
  6. private String desc;
  7. @Override
  8. public Integer getValue() {
  9. return this.value;
  10. }
  11. }

实体属性使用枚举类型

  1. public class User {
  2. /**
  3. * 名字
  4. * 数据库字段: name varchar(20)
  5. */
  6. private String name;
  7. /**
  8. * 年龄,IEnum接口的枚举处理
  9. * 数据库字段:age INT(3)
  10. */
  11. private AgeEnum age;
  12. /**
  13. * 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
  14. * 数据库字段:grade INT(2)
  15. */
  16. private GradeEnum grade;
  17. }

2、配置扫描通用枚举

  • 注意!! spring mvc 配置参考,安装集成 MybatisSqlSessionFactoryBean 枚举包扫描,spring boot 例子配置如下:

示例工程:
👉 mybatisplus-spring-boot

配置文件 resources/application.yml

  1. mybatis-plus:
  2. # 支持统配符 * 或者 ; 分割
  3. typeEnumsPackage: com.baomidou.springboot.entity.enums
  4. ....

如何序列化枚举值为数据库存储值?

Jackson

一、重写toString方法

springboot

  1. @Bean
  2. public Jackson2ObjectMapperBuilderCustomizer customizer(){
  3. return builder -> builder.featuresToEnable(
  4. SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
  5. }

jackson

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

以上两种方式任选其一,然后在枚举中复写toString方法即可.

二、注解处理

  1. public enum GradeEnum {
  2. PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
  3. GradeEnum(int code, String descp) {
  4. this.code = code;
  5. this.descp = descp;
  6. }
  7. @EnumValue
  8. @JsonValue //标记响应json值
  9. private final int code;
  10. }

Fastjson

一、重写toString方法

全局处理方式

  1. FastJsonConfig config = new FastJsonConfig();
  2. config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);

全局部处理方式

  1. @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
  2. private UserStatus status;

以上两种方式任选其一,然后在枚举中复写toString方法即可.

字段类型处理器

类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,本文讲解 mybaits-plus 内置常用类型处理器如何通过TableField注解快速注入到 mybatis 容器中。

示例工程:
👉 mybatis-plus-sample-typehandler

  • JSON 字段类型

    1. @Data
    2. @Accessors(chain = true)
    3. @TableName(autoResultMap = true)
    4. public class User {
    5. private Long id;
    6. ...
    7. /**
    8. * 注意!! 必须开启映射注解
    9. *
    10. * @TableName(autoResultMap = true)
    11. *
    12. * 以下两种类型处理器,二选一 也可以同时存在
    13. *
    14. * 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
    15. */
    16. @TableField(typeHandler = JacksonTypeHandler.class)
    17. // @TableField(typeHandler = FastjsonTypeHandler.class)
    18. private OtherInfo otherInfo;
    19. }

    该注解对应了 XML 中写法为

    1. <result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />

    自动填充功能

    示例工程:
    👉 mybatis-plus-sample-auto-fill-metainfo

  • 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

  • 注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!

    1. public class User {
    2. // 注意!这里需要标记为填充字段
    3. @TableField(.. fill = FieldFill.INSERT)
    4. private String fillField;
    5. ....
    6. }
  • 自定义实现类 MyMetaObjectHandler

    1. @Slf4j
    2. @Component
    3. public class MyMetaObjectHandler implements MetaObjectHandler {
    4. @Override
    5. public void insertFill(MetaObject metaObject) {
    6. log.info("start insert fill ....");
    7. this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
    8. this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
    9. /* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */
    10. //this.setFieldValByName("operator", "Jerry", metaObject);
    11. //this.setInsertFieldValByName("operator", "Jerry", metaObject);
    12. }
    13. @Override
    14. public void updateFill(MetaObject metaObject) {
    15. log.info("start update fill ....");
    16. this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
    17. this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
    18. /* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */
    19. //this.setFieldValByName("operator", "Tom", metaObject);
    20. //this.setUpdateFieldValByName("operator", "Tom", metaObject);
    21. }
    22. }

    注意事项:

  • 字段必须声明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. }

    Sql 注入器

    注入器配置
    全局配置 sqlInjector 用于注入 ISqlInjector 接口的子类,实现自定义方法注入。
    参考默认注入器 DefaultSqlInjector

  • SQL 自动注入器接口 ISqlInjector

    1. public interface ISqlInjector {
    2. /**
    3. * <p>
    4. * 检查SQL是否注入(已经注入过不再注入)
    5. * </p>
    6. *
    7. * @param builderAssistant mapper 信息
    8. * @param mapperClass mapper 接口的 class 对象
    9. */
    10. void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
    11. }

    自定义自己的通用方法可以实现接口 ISqlInjector 也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。

    参考自定义BaseMapper示例)

攻击 SQL 阻断解析器

作用!阻止恶意的全表更新删除

  1. @Bean
  2. public PaginationInterceptor paginationInterceptor() {
  3. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  4. ...
  5. List<ISqlParser> sqlParserList = new ArrayList<>();
  6. // 攻击 SQL 阻断解析器、加入解析链
  7. sqlParserList.add(new BlockAttackSqlParser() {
  8. @Override
  9. public void processDelete(Delete delete) {
  10. // 如果你想自定义做点什么,可以重写父类方法像这样子
  11. if ("user".equals(delete.getTable().getName())) {
  12. // 自定义跳过某个表,其他关联表可以调用 delete.getTables() 判断
  13. return ;
  14. }
  15. super.processDelete(delete);
  16. }
  17. });
  18. paginationInterceptor.setSqlParserList(sqlParserList);
  19. ...
  20. return paginationInterceptor;
  21. }

性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间 该插件 3.2.0 以上版本移除推荐使用第三方扩展 执行SQL分析打印 功能

  • 使用如下:

    1. <plugins>
    2. ....
    3. <!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->
    4. <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
    5. <property name="maxTime" value="100" />
    6. <!--SQL是否格式化 默认false-->
    7. <property name="format" value="true" />
    8. </plugin>
    9. </plugins>
    1. //Spring boot方式
    2. @EnableTransactionManagement
    3. @Configuration
    4. @MapperScan("com.baomidou.cloud.service.*.mapper*")
    5. public class MybatisPlusConfig {
    6. /**
    7. * SQL执行效率插件
    8. */
    9. @Bean
    10. @Profile({"dev","test"})// 设置 dev test 环境开启
    11. public PerformanceInterceptor performanceInterceptor() {
    12. return new PerformanceInterceptor();
    13. }
    14. }

    注意!参数说明:

  • 参数:maxTime SQL 执行最大时长,超过自动停止运行,有助于发现问题。

  • 参数:format SQL SQL是否格式化,默认false。
  • 该插件只用于开发环境,不建议生产环境使用。

    执行 SQL 分析打印

    该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本

示例工程:
👉 mybatis-plus-sample-crud

  • p6spy 依赖引入

Maven:

  1. <dependency>
  2. <groupId>p6spy</groupId>
  3. <artifactId>p6spy</artifactId>
  4. <version>最新版本</version>
  5. </dependency>

Gradle:

  1. compile group: 'p6spy', name: 'p6spy', version: '最新版本'
  • application.yml 配置:

    1. spring:
    2. datasource:
    3. driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    4. url: jdbc:p6spy:h2:mem:test
    5. ...
  • spy.properties 配置:

    1. #3.2.1以上使用
    2. modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
    3. #3.2.1以下使用或者不配置
    4. #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
    5. # 自定义日志打印
    6. logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
    7. #日志输出到控制台
    8. appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
    9. # 使用日志系统记录 sql
    10. #appender=com.p6spy.engine.spy.appender.Slf4JLogger
    11. # 设置 p6spy driver 代理
    12. deregisterdrivers=true
    13. # 取消JDBC URL前缀
    14. useprefix=true
    15. # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
    16. excludecategories=info,debug,result,commit,resultset
    17. # 日期格式
    18. dateformat=yyyy-MM-dd HH:mm:ss
    19. # 实际驱动可多个
    20. #driverlist=org.h2.Driver
    21. # 是否开启慢SQL记录
    22. outagedetection=true
    23. # 慢SQL记录标准 2 秒
    24. outagedetectioninterval=2

    注意!

  • driver-class-name 为 p6spy 提供的驱动类

  • url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
  • 打印出sql为null,在excludecategories增加commit
  • 批量操作不打印sql,去除excludecategories中的batch
  • 批量操作打印重复的问题请使用MybatisPlusLogFactory (3.2.1新增)
  • 该插件有性能损耗,不建议生产环境使用。

    乐观锁插件

    主要适用场景

    意图:
    当要更新一条记录的时候,希望这条记录没有被别人更新
    乐观锁实现方式:

  • 取出记录时,获取当前version

  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

乐观锁配置需要2步 记得两步

1.插件配置

spring xml:

  1. <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>

spring boot:

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

2.注解实体字段 @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 不能复用!!!

    示例

    示例Java代码(参考test case代码)

    1. int id = 100;
    2. int version = 2;
    3. User u = new User();
    4. u.setId(id);
    5. u.setVersion(version);
    6. u.setXXX(xxx);
    7. if(userService.updateById(u)){
    8. System.out.println("Update successfully");
    9. }else{
    10. System.out.println("Update failed due to modified by others");
    11. }

    示例SQL原理

    1. update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

    动态数据源

    MybatisPlus3.0教程 - 图4
    一个基于springboot的快速集成多数据源的启动器

  • Github | 码云Gitee

    简介

    dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
    其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.0.x。最新版为MybatisPlus3.0教程 - 图5
    示例项目 可参考项目下的samples目录。
    示例项目 可参考项目下的samples目录。
    示例项目 可参考项目下的samples目录。

    优势

    网上关于动态数据源的切换的文档有很多,核心只有两种。

  1. 构建多套环境,优势是方便控制也容易集成一些简单的分布式事务,缺点是非动态同时代码量较多,配置难度大。
  2. 基于spring提供原生的 AbstractRoutingDataSource ,参考一些文档自己实现切换。

如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请尝试本动态数据源。

  1. 数据源分组,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  2. 简单集成Druid数据源监控多数据源,简单集成Mybatis-Plus简化单表,简单集成P6sy格式化sql,简单集成Jndi数据源。
  3. 简化Druid和HikariCp配置,提供全局参数配置。
  4. 提供自定义数据源来源(默认使用yml或properties配置)。
  5. 项目启动后能动态增减数据源。
  6. 使用spel动态参数解析数据源,如从session,header和参数中获取数据源。(多租户架构神器)
  7. 多层数据源嵌套切换。(一个业务ServiceA调用ServiceB,ServiceB调用ServiceC,每个Service都是不同的数据源)
  8. 使用正则匹配或spel表达式来切换数据源(实验性功能)。

    劣势

    不能使用多数据源事务(同一个数据源下能使用事务),网上其他方案也都不能提供。
    如果你需要使用到分布式事务,那么你的架构应该到了微服务化的时候了。
    如果呼声强烈,项目达到800 star,作者考虑集成分布式事务。
    PS: 如果您只是几个数据库但是有强烈的需求分布式事务,建议还是使用传统方式自己构建多套环境集成atomic这类,网上百度很多。

    约定

  9. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。

  10. 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
  11. 切换数据源即可是组名,也可是具体数据源名称,切换时默认采用负载均衡机制切换。
  12. 默认的数据源名称为 master ,你可以通过spring.datasource.dynamic.primary修改。
  13. 方法上的注解优先于类上注解。

    建议

    强烈建议在 主从模式 下遵循普遍的规则,以便他人能更轻易理解你的代码。
    主数据库 建议 只执行 INSERT UPDATE DELETE 操作。
    从数据库 建议 只执行 SELECT 操作。

    使用方法

  14. 引入dynamic-datasource-spring-boot-starter。

    1. <dependency>
    2. <groupId>com.baomidou</groupId>
    3. <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    4. <version>${version}</version>
    5. </dependency>
  15. 配置数据源。

    1. spring:
    2. datasource:
    3. dynamic:
    4. primary: master #设置默认的数据源或者数据源组,默认值即为master
    5. datasource:
    6. master:
    7. username: root
    8. password: 123456
    9. driver-class-name: com.mysql.jdbc.Driver
    10. url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
    11. slave_1:
    12. username: root
    13. password: 123456
    14. driver-class-name: com.mysql.jdbc.Driver
    15. url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
    16. slave_2:
    17. username: root
    18. password: 123456
    19. driver-class-name: com.mysql.jdbc.Driver
    20. url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic
    21. #......省略
    22. #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
    1. # 多主多从 纯粹多库(记得设置primary) 混合配置
    2. spring: spring: spring:
    3. datasource: datasource: datasource:
    4. dynamic: dynamic: dynamic:
    5. datasource: datasource: datasource:
    6. master_1: mysql: master:
    7. master_2: oracle: slave_1:
    8. slave_1: sqlserver: slave_2:
    9. slave_2: postgresql: oracle_1:
    10. slave_3: h2: oracle_2:
  16. 使用 @DS 切换数据源。

@DS 可以注解在方法上和类上,同时存在方法注解优先于类上注解
注解在service实现或mapper接口方法上,但强烈不建议同时在service和mapper注解。 (可能会有问题)

注解 结果
没有@DS 默认数据源
@DS(“dsName”) dsName可以为组名也可以为具体某个库的名称
  1. @Service
  2. @DS("slave")
  3. public class UserServiceImpl implements UserService {
  4. @Autowired
  5. private JdbcTemplate jdbcTemplate;
  6. public List<Map<String, Object>> selectAll() {
  7. return jdbcTemplate.queryForList("select * from user");
  8. }
  9. @Override
  10. @DS("slave_1")
  11. public List<Map<String, Object>> selectByCondition() {
  12. return jdbcTemplate.queryForList("select * from user where age >10");
  13. }
  14. }

在mybatis环境下也可注解在mapper接口层。

  1. @DS("slave")
  2. public interface UserMapper {
  3. @Insert("INSERT INTO user (name,age) values (#{name},#{age})")
  4. boolean addUser(@Param("name") String name, @Param("age") Integer age);
  5. @Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}")
  6. boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);
  7. @Delete("DELETE from user where id =#{id}")
  8. boolean deleteUser(@Param("id") Integer id);
  9. @Select("SELECT * FROM user")
  10. @DS("slave_1")
  11. List<User> selectAll();
  12. }

赶紧集成体验一下吧! 如果需要更多功能请继续往下看!


  • Druid集成,MybatisPlus集成,动态增减数据源等等更多更细致的文档在这里 点击查看
  • 项目Javadoc一览 点击查看

    分布式事务

    暂时支持 rabbit 实现可靠消息分布式事务 3.1.1 以上版本

示例工程:
👉 mybatis-plus-sample-dts-rabbit

  • 更多待完善

    多租户 SQL 解析器

  • 这里配合 分页拦截器 使用, spring boot 例子配置如下:

示例工程:
👉 mybatis-plus-sample-tenant
👉 mybatisplus-spring-boot

  1. @Bean
  2. public PaginationInterceptor paginationInterceptor() {
  3. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  4. /*
  5. * 【测试多租户】 SQL 解析处理拦截器<br>
  6. * 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>
  7. */
  8. List<ISqlParser> sqlParserList = new ArrayList<>();
  9. TenantSqlParser tenantSqlParser = new TenantSqlParser();
  10. tenantSqlParser.setTenantHandler(new TenantHandler() {
  11. @Override
  12. public Expression getTenantId(boolean where) {
  13. // 该 where 条件 3.2.0 版本开始添加的,用于分区是否为在 where 条件中使用
  14. // 此判断用于支持返回多个租户 ID 场景,具体使用查看示例工程
  15. return new LongValue(1L);
  16. }
  17. @Override
  18. public String getTenantIdColumn() {
  19. return "tenant_id";
  20. }
  21. @Override
  22. public boolean doTableFilter(String tableName) {
  23. // 这里可以判断是否过滤表
  24. /*
  25. if ("user".equals(tableName)) {
  26. return true;
  27. }*/
  28. return false;
  29. }
  30. });
  31. sqlParserList.add(tenantSqlParser);
  32. paginationInterceptor.setSqlParserList(sqlParserList);
  33. paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
  34. @Override
  35. public boolean doFilter(MetaObject metaObject) {
  36. MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
  37. // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
  38. if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
  39. return true;
  40. }
  41. return false;
  42. }
  43. });
  44. return paginationInterceptor;
  45. }
  • 相关 SQL 解析如多租户可通过 @SqlParser(filter=true) 排除 SQL 解析,注意!!全局配置 sqlParserCache 设置为 true 才生效。(3.1.1开始不再需要这一步)
    1. # 开启 SQL 解析缓存注解生效
    2. mybatis-plus:
    3. global-config:
    4. sql-parser-cache: true

    动态表名 SQL 解析器

    该功能解决动态表名支持 3.1.1 以上版本

简单示例:
👉 mybatis-plus-sample-dynamic-tablename
源码文件:
👉 DynamicTableNameParser

  • 具体使用参考多租户

实现 ITableNameHandler 接口注入到 DynamicTableNameParser 处理器链中,将动态表名解析器注入到 MP 解析链。
注意事项:

  • 原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换
  • 例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等

    MybatisX 快速开发插件

    MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
    安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
    如果各位觉得好用,请为该插件打一个五分好评 哦! 源码地址:MybatisX 源码

    功能

  • Java 与 XML 调回跳转

  • Mapper 方法自动生成 XML MybatisPlus3.0教程 - 图6

    计划支持

  • 连接数据源之后 xml 里自动提示字段

  • sql 增删改查
  • 集成 MP 代码生成
  • 其他

    常见问题

    如何排除非表中字段?

    以下三种方式选择一种即可:

  • 使用 transient 修饰

    1. private transient String noColumn;
  • 使用 static 修饰

    1. private static String noColumn;
  • 使用 TableField 注解

    1. @TableField(exist=false)
    2. private String noColumn;

    排除实体父类属性

    1. /**
    2. * 忽略父类 createTime 字段映射
    3. */
    4. private transient String createTime;

    出现 Invalid bound statement (not found) 异常

    不要怀疑,正视自己,这个异常肯定是你插入的姿势不对……

  • 检查是不是引入 jar 冲突

  • 检查 Mapper.java 的扫描路径

    • 方法一:在 Configuration 类上使用注解 MapperScan

      1. @Configuration
      2. @MapperScan("com.yourpackage.*.mapper")
      3. public class YourConfigClass{
      4. ...
      5. }
    • 方法二:在Configuration类里面,配置MapperScannerConfigurer查看示例

      1. @Bean
      2. public MapperScannerConfigurer mapperScannerConfigurer(){
      3. MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
      4. //可以通过环境变量获取你的mapper路径,这样mapper扫描可以通过配置文件配置了
      5. scannerConfigurer.setBasePackage("com.yourpackage.*.mapper");
      6. return scannerConfigurer;
      7. }
  • 检查是否指定了主键?如未指定,则会导致 selectById 相关 ID 无法操作,请用注解 @TableId 注解表 ID 主键。当然 @TableId 注解可以没有!但是你的主键必须叫 id(忽略大小写)

  • SqlSessionFactory不要使用原生的,请使用MybatisSqlSessionFactory
  • 检查是否自定义了SqlInjector,是否复写了getMethodList()方法,该方法里是否注入了你需要的方法(可参考DefaultSqlInjector)

    自定义 SQL 无法执行

    问题描述:指在 XML 中里面自定义 SQL,却无法调用。本功能同 MyBatis 一样需要配置 XML 扫描路径:

  • Spring MVC 配置(参考mybatisplus-spring-mvc

    1. <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    2. <property name="dataSource" ref="dataSource" />
    3. <property name="typeAliasesPackage" value="xxx.entity" />
    4. <property name="mapperLocations" value="classpath*:/mybatis/*/*.xml"/>
    5. ...
    6. </bean>
  • Spring Boot 配置(参考mybatisplus-spring-boot

    1. mybatis-plus:
    2. mapper-locations: classpath*:/mapper/**/*.xml
  • 对于IDEA系列编辑器,XML 文件是不能放在 java 文件夹中的,IDEA 默认不会编译源码文件夹中的 XML 文件,可以参照以下方式解决:

    • 将配置文件放在 resource 文件夹中
    • 对于 Maven 项目,可指定 POM 文件的 resource
      1. <build>
      2. <resources>
      3. <resource>
      4. <!-- xml放在java目录下-->
      5. <directory>src/main/java</directory>
      6. <includes>
      7. <include>**/*.xml</include>
      8. </includes>
      9. </resource>
      10. <!--指定资源的位置(xml放在resources下,可以不用指定)-->
      11. <resource>
      12. <directory>src/main/resources</directory>
      13. </resource>
      14. </resources>
      15. </build>
      注意!Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)

      启动时异常

  • 异常一:

    1. java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
  • MapperScan 需要排除 com.baomidou.mybatisplus.mapper.BaseMapper 类 及其 子类(自定义公共 Mapper),比如:

    1. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    2. public interface SuperMapper<T> extends BaseMapper<T> {
    3. // your methods
    4. }
  • 异常二:

    1. Injection of autowired
  • 原因:低版本不支持泛型注入,请升级 Spring 版本到 4+ 以上。

  • 异常三:

    1. java.lang.NoSuchMethodError: org.apache.ibatis.session.Configuration.getDefaultScriptingLanguageInstance() Lorg/apache/ibatis/scripting/LanguageDriver
  • 版本引入问题:3.4.1 版本里没有,3.4.2 里面才有!

    关于 Long 型主键填充不生效的问题

    检查是不是用了long而不是Long
    long类型默认值为 0,而 MP 只会判断是否为 null

    ID_WORKER 生成主键太长导致 js 精度丢失

    JavaScript 无法处理 Java 的长整型 Long 导致精度丢失,具体表现为主键最后两位永远为 0,解决思路: Long 转为 String 返回

  • FastJson 处理方式

    1. @Override
    2. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    3. FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
    4. FastJsonConfig fjc = new FastJsonConfig();
    5. // 配置序列化策略
    6. fjc.setSerializerFeatures(SerializerFeature.BrowserCompatible);
    7. fastJsonConverter.setFastJsonConfig(fjc);
    8. converters.add(fastJsonConverter);
    9. }
  • JackJson 处理方式

    • 方式一

      1. // 注解处理,这里可以配置公共 baseEntity 处理
      2. @JsonSerialize(using=ToStringSerializer.class)
      3. public long getId() {
      4. return id;
      5. }
    • 方式二

      1. // 全局配置序列化返回 JSON 处理
      2. final ObjectMapper objectMapper = new ObjectMapper();
      3. SimpleModule simpleModule = new SimpleModule();
      4. simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
      5. objectMapper.registerModule(simpleModule);
  • 比较一般的处理方式:增加一个 public String getIdStr() 方法,前台获取 idStr

    插入或更新的字段有 空字符串 或者 null

    FieldStrategy 有三种策略:

  • IGNORED:忽略

  • NOT_NULL:非 NULL,默认策略
  • NOT_EMPTY:非空

当用户有更新字段为 空字符串 或者 null 的需求时,需要对 FieldStrategy 策略进行调整:

  • 方式一:调整全局的验证策略
    注入配置 GlobalConfiguration 属性 fieldStrategy
  • 方式二:调整字段验证注解
    根据具体情况,在需要更新的字段中调整验证注解,如验证非空:

    1. @TableField(strategy=FieldStrategy.NOT_EMPTY)
  • 方式三:使用 UpdateWrapper (3.x)
    使用以下方法来进行更新或插入操作:

    1. //updateAllColumnById(entity) // 全部字段更新: 3.0已经移除
    2. mapper.update(
    3. new User().setName("mp").setAge(3),
    4. Wrappers.<User>lambdaUpdate()
    5. .set(User::getEmail, null) //把email设置成null
    6. .eq(User::getId, 2)
    7. );
    8. //也可以参考下面这种写法
    9. mapper.update(
    10. null,
    11. Wrappers.<User>lambdaUpdate()
    12. .set(User::getAge, 3)
    13. .set(User::getName, "mp")
    14. .set(User::getEmail, null) //把email设置成null
    15. .eq(User::getId, 2)
    16. );

    字段类型为 bittinyint(1) 时映射为 boolean 类型

    默认mysql驱动会把tinyint(1)字段映射为boolean: 0=false, 非0=true
    MyBatis 是不会自动处理该映射,如果不想把tinyint(1)映射为boolean类型:

  • 修改类型tinyint(1)为tinyint(2)或者int

  • 需要修改请求连接添加参数 tinyInt1isBit=false,如下:
    1. jdbc:mysql://127.0.0.1:3306/mp?tinyInt1isBit=false

    出现 2 个 limit 语句

    原因:配了 2 个分页拦截器! 检查配置文件或者代码,只留一个!

    insert 后如何返回主键

    insert 后主键会自动 set 到实体的 ID 字段,所以你只需要 getId() 就好

    MP 如何查指定的几个字段

    EntityWrapper.sqlSelect 配置你想要查询的字段
    1. //2.x
    2. EntityWrapper<H2User> ew = new EntityWrapper<>();
    3. ew.setSqlSelect("test_id as id, name, age");//只查询3个字段
    4. List<H2User> list = userService.selectList(ew);
    5. for(H2User u:list){
    6. Assert.assertNotNull(u.getId());
    7. Assert.assertNotNull(u.getName());
    8. Assert.assertNull(u.getPrice()); // 这个字段没有查询出来
    9. }
    10. //3.x
    11. mapper.selectList(
    12. Wrappers.<User>lambdaQuery()
    13. .select(User::getId, User::getName)
    14. );
    15. //或者使用QueryWrapper
    16. mapper.selectList(
    17. new QueryWrapper<User>()
    18. .select("id","name")
    19. );

    mapper 层二级缓存问题

    我们建议缓存放到 service 层,你可以自定义自己的 BaseServiceImpl 重写注解父类方法,继承自己的实现。
    当然如果你是一个极端分子,请使用 CachePaginationInterceptor 替换默认分页,这样支持分页缓存。

    mapper 层二级缓存刷新问题

    如果你按照 mybatis 的方式配置第三方二级缓存,并且使用 2.0.9 以上的版本,则会发现自带的方法无法更新缓存内容,那么请按如下方式解决(二选一):
    1.在代码中 mybatis 的 mapper 层添加缓存注释,声明 implementation 或 eviction 的值为 cache 接口的实现类
    1. @CacheNamespace(implementation=MybatisRedisCache.class,eviction=MybatisRedisCache.class)
    2. public interface DataResourceMapper extends BaseMapper<DataResource>{}
    2.在对应的 mapper.xml 中将原有注释修改为链接式声明,以保证 xml 文件里的缓存能够正常
    1. <cache-ref namespace="com.mst.cms.dao.DataResourceMapper"></cache-ref>

    Cause: org.apache.ibatis.type.TypeException:Error setting null for parameter #1 with JdbcType OTHER

    配置 jdbcTypeForNull=NULL Spring Bean 配置方式:

  1. MybatisConfiguration configuration = new MybatisConfiguration();
  2. configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
  3. configuration.setJdbcTypeForNull(JdbcType.NULL);
  4. configuration.setMapUnderscoreToCamelCase(true);//开启下划线转驼峰
  5. sqlSessionFactory.setConfiguration(configuration);

yml 配置

  1. mybatis-plus:
  2. configuration:
  3. jdbc-type-for-null: 'null'

自定义 sql 里使用 Page 对象传参无法获取

Page 对象是继承 RowBounds,是 Mybatis 内置对象,无法在 mapper 里获取 请使用自定义 Map/对象,或者通过@Param(“page”) int page,size 来传参

如何使用:【Map下划线自动转驼峰】

指的是:resultType="java.util.Map"

  • spring boot

    1. @Bean
    2. public ConfigurationCustomizer configurationCustomizer() {
    3. return i -> i.setObjectWrapperFactory(new MybatisMapWrapperFactory());
    4. }

    在 wrapper 中如何使用 limit 限制 SQL

    1. // 取 1 条数据
    2. wrapper.last("limit 1");

    通用 insertBatch 为什么放在 service 层处理

  • SQL 长度有限制海量数据量单条 SQL 无法执行,就算可执行也容易引起内存泄露 JDBC 连接超时等

  • 不同数据库对于单条 SQL 批量语法不一样不利于通用
  • 目前的解决方案:循环预处理批量提交,虽然性能比单 SQL 慢但是可以解决以上问题。

    逻辑删除下 自动填充 功能没有效果

  • 自动填充的实现方式是填充到入参的entity内,由于baseMapper提供的删除接口入参不是entity所以逻辑删除无效

  • 如果你想要使用自动填充有效:
    • 方式一: 使用update方法:UpdateWrapper.set("logicDeleteColumn","deleteValue")
    • 方式二: 配合Sql注入器
      并使用我们提供的com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill
      注意该类只能填充指定了自动填充的字段,其他字段无效
  • 方式2下: Java Config Bean 配置

    1. 配置自定义的 SqlInjector

      1. @Bean
      2. public LogicSqlInjector logicSqlInjector(){
      3. return new LogicSqlInjector() {
      4. /**
      5. * 注入自定义全局方法
      6. */
      7. @Override
      8. public List<AbstractMethod> getMethodList() {
      9. List<AbstractMethod> methodList = super.getMethodList();
      10. methodList.add(new LogicDeleteByIdWithFill());
      11. return methodList;
      12. }
      13. };
      14. }
    2. 配置自己的全局 baseMapper 并使用

      1. public interface MyBaseMapper<T> extends BaseMapper<T> {
      2. /**
      3. * 自定义全局方法
      4. */
      5. int deleteByIdWithFill(T entity);
      6. }

      3.x数据库关键字如何处理?

      在以前的版本是自动识别关键字进行处理的,但是3.x移除了这个功能。

  1. 不同的数据库对关键字的处理不同,很难维护。
  2. 在数据库设计时候本身不建议使用关键字。
  3. 交由用户去处理关键字。
    1. @TableField(value = "`status`")
    2. private Boolean status;

    MybatisPlusException: Your property named “xxx” cannot find the corresponding database column name!

    针对3.1.1以及后面的版本:
    现象: 单元测试没问题,启动服务器进行调试就出现这个问题
    原因: dev-tools, 3.1.1+针对字段缓存,使用.class来作为key替换了原来的className, 而使用dev-tools会把.class使用不同的classLoader加载,导致可能出现找不到的情况
    解决方案: 去掉dev-tools插件

    Error attempting to get column ‘create_time’ from result set. Cause: java.sql.SQLFeatureNotSupportedException

    3.1.0之前版本没问题,针对3.1.1以及后续版本出现上述问题
    现象: 集成druid数据源,使用3.1.0之前版本没问题,升级mp到3.1.1+后,运行时报错:java.sql.SQLFeatureNotSupportedException
    原因: mp3.1.1+使用了新版jdbc,LocalDateTime等新日期类型处理方式升级,但druid在1.1.21版本之前不支持,参考issue
    解决方案: 1. 升级druid到1.1.21解决这个问题;2.保持mp版本3.1.0;3.紧跟mp版本,换掉druid数据源

    mp版本从3.1.0及以下版本升级到高版本,JDK8日期新类型LocalDateTime等无法映射(报错)

    MP_3.1.0及之前的版本,依赖的是mybatis 3.5.0,
    MP_3.1.1升级了mybatis的依赖到3.5.1, 而mybatis 3.5.1 对于新日期类型,需要JDBC driver支持JDBC 4.2 API.
    如果你的jdbc驱动版本不支持,那么就会出现新日期类型报错。
    参考 blog.mybatis.org
    1. There is one backward incompatible changes since 3.5.0.
    2. Because of the fix for #1478 , LocalDateTypeHandler, LocalTimeTypeHandler and LocalDateTimeTypeHandler now require a JDBC driver that supports JDBC 4.2 API.
    3. [EDIT] These type handlers no longer work with Druid. Please see #1516 .

    Failed to bind properties under ‘mybatis-plus.configuration.incomplete-result-maps[0].assistant.configuration.mapped-statements[0].parameter-map.parameter-mappings[0]’ to org.apache.ibatis.mapping.ParameterMapping

    springboot 2.2.0 之前无此问题, springboot 2.2.0 出现此问题
    现象: 1.本地启动无问题,打成war包部署到服务器报此问题
    原因:springboot 2.2.0 构造器注入的问题, mybatis 私有构造器不能绑定属性, 造成依赖mybatis的框架比如MP报错 [参考issue](https://github.com/spring-projects/spring-boot/issues/18670) 此问题已在springboot2.2.1中修复
    解决方案:1.将springboot降级到2.1.x或升级到2.2.1起 (建议springboot2.2.2)