一、基础部分
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 操作智能分析阻断,也可自定义拦截规则,预防误操作 */
1.2、适用数据库
MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库,星瑞格数据库
1.3、框架结构
1.4、代码托管
https://github.com/baomidou/mybatis-plus https://gitee.com/baomidou/mybatis-plus
二、快速上手
2.1、工程搭建
初始化工程
创建一个空的 Spring Boot 工程(工程将以 H2 作为默认数据库进行演示)提示:可以使用 Spring Initializer (opens new window)快速初始化一个 Spring Boot 工程
添加依赖
引入 Spring Boot Starter 父工程:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5+ 版本</version> <relativePath/> </parent>
引入 spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、h2 依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- MybatisPlus驱动包 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency> <!-- MySQL驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
配置
在 application.yml 配置文件中添加 MyQSL 数据库的相关配置:
# DataSource Config spring: datasource: driver-class-name: com.mysql.cj.jdbc.Oriver url: jabc:mysql://localhost:3306/db_mp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: test
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:
@SpringBootApplication // 扫描加载mapper接口 @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
mapper接口
/** 自定义Mapper接口继承BaseMapper<T>接口,即可使用MP */ public interface UserMapper extends BaseMapper<User>{}
测试类
@RunWith(SpringRunner.class) @SpringBootTest public class ApplicationTests { //注入userMapper接口 @Resource private UserMapper userMapper; @Test public void test(){ //调用mp底层查询方法 List<User> users = userMapper.selectList(null); for(User user:users){ Sysatem.out.printIn(user); } } }
2.2、安装
全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调用,所以安装集成 MP3.0 要求如下:JDK 8+
Release
Spring BootMaven:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency>
Gradle:
//Gradle Version:<4.1 compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '最新版本' //Gradle Version:>=4.1 (The function compile has been deprecated since Gradle 4.10, and removed since Gradle 7.0. Please use implementation instead.) implementation 'com.baomidou:mybatis-plus-boot-starter:最新版本'
SpringMaven:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>最新版本</version> </dependency>
Gradle:
//Gradle Version:<4.1 compile group: 'com.baomidou', name: 'mybatis-plus', version: '最新版本' //Gradle Version:>=4.1 (The function compile has been deprecated since Gradle 4.10, and removed since Gradle 7.0. Please use implementation instead.) implementation 'com.baomidou:mybatis-plus:最新版本'
注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。
Snapshot快照 SNAPSHOT 版本需要添加仓库,且版本号为快照版本 点击查看最新快照版本号 (opens new window)Maven:
<repository> <id>snapshots</id> <url>https://oss.sonatype.org/content/repositories/snapshots/</url> </repository>
Gradle:
repositories { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } }
2.3、配置
MyBatis-Plus 的配置异常的简单,我们仅需要一些简单的配置即可使用 MyBatis-Plus 的强大功能!Spring Boot 工程配置 MapperScan 注解
@SpringBootApplication @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Spring 工程配置 MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/> </bean>
调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean>
通常来说,一般的简单工程,通过以上配置即可正常使用 MyBatis-Plus,具体可参考以下项目:Spring Boot 快速启动示例:https://github.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-quickstartSpring MVC 快速启动示例:https://github.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-quickstart-springmvc同时 MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程,大家可根据自己的项目按需取用,详细配置请参考使用配置一文。针对复杂的表结构,我们也提供了丰富的字段注解以满足大家的特殊需求,详情请参考注解一文。
2.4、mybatis plus的使用
通用的CRUD的使用
提出问题:假设已存在一张t_employee表,切已有对的实体类Employee,实现t_employee表的CRUD操作,我们需要做什么?
实现方法:
(1)、基于Mybatis
需要编写EmployeeMapper接口,并手动编写CRUD方法
提供EmployeeMapper.xml映射文件,并手动编写每个方法对应的SQL语句
(2)、基于MP
只需要创建EmployeeMapper接口,并继承BaseMapper接口即可
若需要完成所有操作,不需要创建SQL
数据库文件:创建t_employee表,对应实体类Employee
问题:表名与实体名不一致,列名与属性名不一致是否可以成功运行?
注意:表名与实体名不一致时,需要使用@TableName注解指定表名
insert新增操作:
MP源码:
/* 插入一条记录 @param entity 实体对象 */ int insert(T entity);
实体类:
遵循JavaBean规范编写实体类,提供get\set方法及toString()方法
Mapper接口:
在dao包下创建EmployeeMapper接口,继承BaseMapper接口,无需编写xml映射文件
public interface EmployeeMapper extends BaseMapper<Emplyee>{}
测试新增:
在test包下创建新的测试类TestEmployee
@RunWith(SpringRunner.class) @SpringBootTest public class TestEmployee { @Resource private EmployeeMapper employeeMapper; @Test public void testInsert(){ Employee employee = new Employee(); employee.setAge(20); //新增 int count = employeeMapper.insert(employee); if(count>0){ System.out.println("新增成功,主键id为:"+employee.getId()); }else{ System.out.println("新增失败"); } } }
错误1-没有设置主键
运行测试后,发现出现以下错误: org.mybatis.spring.MyBatisSystemException:nested exception is org.apache.ibatis.reflection.ReflectionException:Could not set property 'id' of 'class com.kazu.mybatisplus.entity.Employee' with value '1158219669003407361' Cause:java.lang.IllegalArgumentException:argument type mismatch 解决办法: 在实体类中的主键属性加入注解@TableId //主键生成策略,value属性可选,当属性名与表中的列名不一致必填 @TableId(value="id",type=IdType.AUTO)//type:配置主键生成策略,IdType.AUTO表示自增 private Integer id;
错误2-表名与实体类名不一致
运行测试后,发现出现以下错误: org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Table 'db_mybatisplus.employee' doesn`t exist ### The error may exist in com/kazu/mybatisplus/dao/EmployeeMapper.java (best guess) ### The error may involve com.kazu.mybatisplus.dao.EmployeeMapper.insert-Inline ### The error occurred while setting parameters ### SQL:INSERT INTO employee (user_name,email,gander,age) VALUES (?,?,?,?) ### Cause: java.sql.SQLSyntaxErrorException: Table 'db_mybatisplus.employee' doesn`t exist; bad SQL grannar [];nested exception is java.sqlSQLSyntaxErrorException:table 'db_mybatisplus.employee' doesn`texist 解决办法: 在实体类名称上加入注解@TableName //名称一致时,此注解可以省略 @TableName(value = "t_employee") public class Employee{}
错误3-列名与属性名不一致
运行测试后,发现出现以下错误: org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'username' in 'fieId list' ### The error may exist in com/kazu/mybatisplus/dao/EmployeeMapper.java (best guess) ### The error may involve com.kazu.mybatisplus.dao.EmployeeMapper.insert-Inline ### The error occurred while setting parameters ### SQL:INSERT INTO employee (user_name,email,gander,age) VALUES (?,?,?,?) ### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'username' in 'fieId list'; bad SQL grannar [];nested exception is java.sqlSQLSyntaxErrorException:table 'db_mybatisplus.employee' doesn`texist 解决办法: 在实体类属性上加入注解@TableFieId 注意:当表中的列名与实体类中的属性值不一致时,属性名遵循驼峰命名法,可以自动映射,无需加入@TableFieId注解,如:列名为user_name,属性名为userName【该情况下不需要加注解】 //@TableFieId注解,当表中与实体的属性名不一致,需要加入该注解 //value属性:该属性可选,档表中的列名与实体类中的属性不一致时,该属性必须且填写的是列名 @TableFieId(value="user_name") private String username;
2.5、注解
本文将介绍 MybatisPlus 注解包相关类详解(更多详细描述可点击查看源码注释)注解类包源码:mybatis-plus: mybatis 增强工具包,简化 CRUD 操作。 文档 http://baomidou.com低代码组件库 http://aizuda.com - Gitee.com
@TableName
描述:表名注解,标识实体类对应的表使用位置:实体类
@TableName("sys_user") public class User { private Long id; private String name; private Integer age; private String email; }
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
关于 autoResultMap 的说明:MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上),请注意以下内容:因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:1、定义在 resultMap 里,作用于查询结果的封装2、定义在 insert 和 update 语句的 #{property} 中的 property 后面(例:#{property,typehandler=xxx.xxx.xxx}),并且只作用于当前 设置值除了以上两种直接指定 typeHandler 的形式,MyBatis 有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的 property 类型去找其对应的 typeHandler 并使用。
@TableId
描述:主键注解
使用位置:实体类主键字段
@TableName("sys_user") public class User { @TableId private Long id; private String name; private Integer age; private String email; }
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
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
描述:字段注解(非主键)
@TableName("sys_user") public class User { @TableId private Long id; @TableField("nickname") private String name; private Integer age; private String email; }
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,参考 |
update | String | 否 | "" | 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性) |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.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 | 否 | "" | 指定小数点后保留的位数 |
关于jdbcType
和typeHandler
以及numericScale
的说明:
numericScale
只生效于 update 的 sql. jdbcType
和typeHandler
如果不配合@TableName#autoResultMap = true
一起使用,也只生效于 update 的 sql. 对于typeHandler
如果你的字段类型和 set 进去的类型为equals
关系,则只需要让你的typeHandler
让 Mybatis 加载到即可,不需要使用注解
FieldStrategy
值 | 描述 |
---|---|
IGNORED | 忽略判断 |
NOT_NULL | 非 NULL 判断 |
NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断) |
DEFAULT | 追随全局配置 |
NEVER | 不加入SQL |
FieldFill
值 | 描述 |
---|---|
DEFAULT | 默认不处理 |
INSERT | 插入时填充字段 |
UPDATE | 更新时填充字段 |
INSERT_UPDATE | 插入和更新时填充字段 |
@Version
描述:乐观锁注解、标记 @Version
在字段上
@EnumValue
描述:普通枚举类注解(注解在枚举字段上)
@TableLogic
描述:表字段逻辑处理注解(逻辑删除)
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 逻辑未删除值 |
delval | String | 否 | "" | 逻辑删除值 |
@SqlParser
租户注解
@KeySequence
描述:序列主键策略 oracle
属性:value、dbType
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 序列名 |
dbType | Enum | 否 | DbType.OTHER | 数据库类型,未配置默认使用注入 IKeyGenerator 实现,多个实现必须指定 |
@InterceptorIgnore
-
value
值为1
|yes
|on
视为忽略,例如@InterceptorIgnore(tenantLine = "1")
-
value
值为0
|false
|off
|空值不变
视为正常执行。
see 插件主体
@OrderBy
描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
isDesc | boolean | 否 | true | 是否倒序查询 |
sort | short | 否 | Short.MAX_VALUE | 数字越小越靠前 |
三、核心功能
3.1、代码生成器(新)
3.1.1、简介
注意:适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!3.5.1 以下的请参考 代码生成器旧
目前支持两套生成的方式,一套使用SQL查询的方式是兼容旧的代码生成器核心逻辑使用,另一套使用驱动规范来读取元数据的方式,默认的使用元数据查询方式来生成代码,
建议: 如果是已知数据库(无版本兼容问题下)请继续按照原有的SQL查询方式继续使用(见如下代码),如果是新项目或者不支持的数据库类型可以使用元数据查询的方式来进行生成.
// MYSQL 示例 切换至SQL查询方式,需要指定好dbQuery与typeConvert来生成 .dataSourceConfig(builder -> builder.databaseQueryClass(SQLQuery.class).typeConvert(new MySqlTypeConvert()).dbQuery(new MySqlQuery())
查询方式 | 优点 | 缺点 | 备注 |
---|---|---|---|
DefaultQuery (元数据查询) | 根据通用接口读取数据库元数据相关信息,对数据库通用性会好点 | 依赖数据库厂商驱动实现 | 默认方式,对部分类型处理不太好 |
SQLQuery (SQL查询) | 需要根据数据库编写对应表,主键,字段获取等查询语句 | 通用性不强,同数据库厂商不同版本可能会存在兼容问题(例如H2数据库只支持1.X版本) | 后期不再维护 |
元数据查询已知问题:
1.不支持NotLike的方式反向生成表:
不同于SQL过滤,这种需要获取数据库模式下所有表来生成,不考虑支持.
2.无法读取表注释: Mysql链接增加属性 remarks=true&useInformationSchema=true
Oracle链接增加属性 remarks=true也有些驱动版本说是增加remarksReporting=true
Sqlserver: 驱动不支持 getColumns Method (SQLServerDatabaseMetaData) - JDBC Driver for SQL Server | Microsoft Learn
3.PostgreSQL部分类型不好处理: json,jsonb,uuid,xml,money类型生成Object或Double
json,jsonb,uuid,xml 生成了Object,Mybatis下生成String也无法正常处理映射,只能转换成项目自定义的类型配合自定义TypeHandler来处理
money生成了Double,这个类型无法处理,就算使用驱动自带的PGmoney类型mybatis处理也会出现问题,处理方式同上,不过这种数据类型最好别用
转换成自己需要的类型可以扩展typeConvertHandler来处理(3.5.3.3后增加了typeName获取)
4.Mysql下tinyint字段转换:
当字段长度为1时,无法转换Boolean字段, 建议在指定数据库连接的时候把 &tinyInt1isBit=true 增加上去
当字段长度大于1时,默认转换成Byte,符合类型长度范围,如果想继续转换成Integer.
.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> { // 兼容旧版本转换成Integer if (JdbcType.TINYINT == metaInfo.getJdbcType()) { return DbColumnType.INTEGER; } return typeRegistry.getColumnType(metaInfo); })
3.1.2、 快速入门
安装
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>最新版本</version> </dependency>
注意:当前包未传递依赖 MP 包,需要自己引入!
使用
快速生成
FastAutoGenerator.create("url", "username", "password") .globalConfig(builder -> { builder.author("baomidou") // 设置作者 .enableSwagger() // 开启 swagger 模式 .fileOverride() // 覆盖已生成文件 .outputDir("D://"); // 指定输出目录 }) .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> { int typeCode = metaInfo.getJdbcType().TYPE_CODE; if (typeCode == Types.SMALLINT) { // 自定义类型转换 return DbColumnType.INTEGER; } return typeRegistry.getColumnType(metaInfo); })) .packageConfig(builder -> { builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名 .moduleName("system") // 设置父包模块名 .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径 }) .strategyConfig(builder -> { builder.addInclude("t_simple") // 设置需要生成的表名 .addTablePrefix("t_", "c_"); // 设置过滤表前缀 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute();
交互式生成
FastAutoGenerator.create(DATA_SOURCE_CONFIG) // 全局配置 .globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride()) // 包配置 .packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?"))) // 策略配置 .strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all"))) .controllerBuilder().enableRestStyle().enableHyphenStyle() .entityBuilder().enableLombok().addTableFills( new Column("create_time", FieldFill.INSERT) ).build()) /* 模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker .templateEngine(new BeetlTemplateEngine()) .templateEngine(new FreemarkerTemplateEngine()) */ .execute(); // 处理 all 情况 protected static List<String> getTables(String tables) { return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(",")); }
配置
3.2、代码生成器(旧)
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 public class CodeGenerator { /** * <p> * 读取控制台内容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("jobob"); gc.setOpen(false); // gc.setSwagger2(true); 实体属性 Swagger2 注解 mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("密码"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名")); pc.setParent("com.baomidou.ant"); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity // String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // 判断自定义文件夹是否需要创建 checkDir("调用默认方法创建的目录,自定义目录用"); if (fileType == FileType.MAPPER) { // 已经生成 mapper 文件判断存在,不想重新生成返回 false return !new File(filePath).exists(); } // 允许生成模板文件 return true; } }); */ cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 配置自定义输出模板 //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!"); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // 公共父类 strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!"); // 写于父类中的公共字段 strategy.setSuperEntityColumns("id"); strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }
使用教程
添加依赖
MyBatis-Plus 从 3.0.3
之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:
添加 代码生成器 依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.0</version> </dependency>
添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。
Velocity(默认):
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>最新版本</version> </dependency>
Freemarker:
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>最新版本</version> </dependency>
Beetl:
<dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl</artifactId> <version>最新版本</version> </dependency>
注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。
AutoGenerator generator = new AutoGenerator(); // set freemarker engine generator.setTemplateEngine(new FreemarkerTemplateEngine()); // set beetl engine generator.setTemplateEngine(new BeetlTemplateEngine()); // set custom engine (reference class is your custom engine class) generator.setTemplateEngine(new CustomTemplateEngine()); // other config ...
编写配置
MyBatis-Plus 的代码生成器提供了大量的自定义参数供用户选择,能够满足绝大部分人的使用需求。
配置 GlobalConfig
GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java"); globalConfig.setAuthor("jobob"); globalConfig.setOpen(false);
配置 DataSourceConfig
DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8"); dataSourceConfig.setDriverName("com.mysql.jdbc.Driver"); dataSourceConfig.setUsername("root"); dataSourceConfig.setPassword("password");
更多生成器配置请移步至 代码生成器配置旧 查看。
自定义模板引擎
请继承类 com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine
自定义代码模板
//指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm) //注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别 TemplateConfig templateConfig = new TemplateConfig() .setEntity("templates/entity2.java"); AutoGenerator mpg = new AutoGenerator(); //配置自定义模板 mpg.setTemplate(templateConfig);
自定义属性注入
InjectionConfig injectionConfig = new InjectionConfig() { //自定义属性注入:abc //在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性 @Override public void initMap() { Map<String, Object> map = new HashMap<>(); map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp"); this.setMap(map); } }; AutoGenerator mpg = new AutoGenerator(); //配置自定义属性注入 mpg.setCfg(injectionConfig);
entity2.java.ftl 自定义属性注入abc=${cfg.abc} entity2.java.vm 自定义属性注入abc=$!{cfg.abc}
字段其他信息查询注入
new DataSourceConfig().setDbQuery(new MySqlQuery() { /** * 重写父类预留查询自定义字段<br> * 这里查询的 SQL 对应父类 tableFieldsSql 的查询字段,默认不能满足你的需求请重写它<br> * 模板中调用: table.fields 获取所有字段信息, * 然后循环字段获取 field.customMap 从 MAP 中获取注入字段如下 NULL 或者 PRIVILEGES */ @Override public String[] fieldCustom() { return new String[]{"NULL", "PRIVILEGES"}; } })
四、CRUD接口
4.1 Service CRUD 接口
说明:
-
通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用
get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分Mapper
层避免混淆, -
泛型
T
为任意实体对象 -
建议如果存在自定义通用 Service 方法的可能,请创建自己的
IBaseService
继承Mybatis-Plus
提供的基类 -
对象
Wrapper
为 条件构造器
4.1.1 Save
// 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection<T> entityList); // 插入(批量) boolean saveBatch(Collection<T> entityList, int batchSize);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 插入批次数量 |
4.1.2 SaveOrUpdate
// TableId 注解存在更新记录,否插入一条记录 boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
Wrapper<T> | updateWrapper | 实体对象封装操作类 UpdateWrapper |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 插入批次数量 |
4.1.3 Remove
// 根据 queryWrapper 设置的条件,删除记录 boolean remove(Wrapper<T> queryWrapper); // 根据 ID 删除 boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录 boolean removeByMap(Map<String, Object> columnMap); // 删除(根据ID 批量删除) boolean removeByIds(Collection<? extends Serializable> idList);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体包装类 QueryWrapper |
Serializable | id | 主键 ID |
Map<String, Object> | columnMap | 表字段 map 对象 |
Collection<? extends Serializable> | idList | 主键 ID 列表 |
4.1.4 UpDate
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset boolean update(Wrapper<T> updateWrapper); // 根据 whereWrapper 条件,更新记录 boolean update(T updateEntity, Wrapper<T> whereWrapper); // 根据 ID 选择修改 boolean updateById(T entity); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList, int batchSize);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体包装类 QueryWrapper |
T | entity | 实体对象 |
Collection<T> | entityList | 实体对象集合 |
int | batchSize | 更新批次数量 |
4.1.5 Get
// 根据 ID 查询 T getById(Serializable id); // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1") T getOne(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 T getOne(Wrapper<T> queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录 Map<String, Object> getMap(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Serializable | id | 主键 ID |
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
boolean | throwEx | 有多个 result 是否抛出异常 |
T | entity | 实体对象 |
Function<? super Object, V> | mapper | 转换函数 |
4.1.6 List
// 查询所有 List<T> list(); // 查询列表 List<T> list(Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) Collection<T> listByIds(Collection<? extends Serializable> idList); // 查询(根据 columnMap 条件) Collection<T> listByMap(Map<String, Object> columnMap); // 查询所有列表 List<Map<String, Object>> listMaps(); // 查询列表 List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper); // 查询全部记录 List<Object> listObjs(); // 查询全部记录 <V> List<V> listObjs(Function<? super Object, V> mapper); // 根据 Wrapper 条件,查询全部记录 List<Object> listObjs(Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录 <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
Collection<? extends Serializable> | idList | 主键 ID 列表 |
Map<String, Object> | columnMap | 表字段 map 对象 |
Function<? super Object, V> | mapper | 转换函数 |
4.1.7 Page
// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); // 无条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page); // 条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
IPage<T> | page | 翻页对象 |
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
4.1.8 Count
// 查询总记录数 int count(); // 根据 Wrapper 条件,查询总记录数 int count(Wrapper<T> queryWrapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体对象封装操作类 QueryWrapper |
query
// 链式查询 普通 QueryChainWrapper<T> query(); // 链式查询 lambda 式。注意:不支持 Kotlin LambdaQueryChainWrapper<T> lambdaQuery(); // 示例: query().eq("column", value).one(); lambdaQuery().eq(Entity::getId, value).list();
update
// 链式更改 普通 UpdateChainWrapper<T> update(); // 链式更改 lambda 式。注意:不支持 Kotlin LambdaUpdateChainWrapper<T> lambdaUpdate(); // 示例: update().eq("column", value).remove(); lambdaUpdate().eq(Entity::getId, value).update(entity);
4.2 Mapper CRUD接口
说明:
-
通用 CRUD 封装BaseMapper (opens new window)接口,为
Mybatis-Plus
启动时自动解析实体表关系映射转换为Mybatis
内部对象注入容器 -
泛型
T
为任意实体对象 -
参数
Serializable
为任意类型主键Mybatis-Plus
不推荐使用复合主键约定每一张表都有自己的唯一id
主键 -
对象
Wrapper
为 条件构造器
4.2.1 Insert
// 插入一条记录 int insert(T entity);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
4.2.2 Delete
// 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 删除(根据ID 批量删除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除 int deleteById(Serializable id); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | wrapper | 实体对象封装操作类(可以为 null) |
Collection<? extends Serializable> | idList | 主键 ID 列表(不能为 null 以及 empty) |
Serializable | id | 主键 ID |
Map<String, Object> | columnMap | 表字段 map 对象 |
4.2.3 Update
// 根据 whereWrapper 条件,更新记录 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper); // 根据 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
使用提示:在调用updateById
方法前,需要在T entity
(对应的实体类)中的主键属性上加上@TableId
注解。
参数说明
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 (set 条件值,可为 null) |
Wrapper<T> | updateWrapper | 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) |
4.2.4 Select
// 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 entity 条件,查询全部记录(并翻页) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Serializable | id | 主键 ID |
Wrapper<T> | queryWrapper | 实体对象封装操作类(可以为 null) |
Collection<? extends Serializable> | idList | 主键 ID 列表(不能为 null 以及 empty) |
Map<String, Object> | columnMap | 表字段 map 对象 |
IPage<T> | page | 分页查询条件(可以为 RowBounds.DEFAULT) |
4.3 mapper 层 选装件
说明:
选装件位于 com.baomidou.mybatisplus.extension.injector.methods
包下 需要配合Sql 注入器使用,案例(opens new window)使用详细见源码注释
AlwaysUpdateSomeColumnById(opens new window)
int alwaysUpdateSomeColumnById(T entity);
insertBatchSomeColumn(opens new window)
int insertBatchSomeColumn(List<T> entityList);
logicDeleteByIdWithFill(opens new window)
int logicDeleteByIdWithFill(T entity);
4.4 ActiveRecord 模式
说明:
-
实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
需要项目中已注入对应实体的BaseMapper
操作步骤:
class User extends Model<User>{ // fields... }
-
调用
CRUD
方法(演示部分api,仅供参考)
User user = new User(); user.insert(); user.selectAll(); user.updateById(); user.deleteById(); // ...
4.5 SimpleQuery 工具类
说明:
-
对
selectList
查询后的结果用Stream
流进行了一些封装,使其可以返回一些指定结果,简洁了api的调用 -
需要项目中已注入对应实体的BaseMapper
-
使用方式见: 测试用例(opens new window)
-
对于下方参数
peeks
,其类型为Consumer...
,可一直往后叠加操作例如:List<Long> ids = SimpleQuery.list(Wrappers.lambdaQuery(), Entity::getId, System.out::println, user -> userNames.add(user.getName()));
4.5.1 keyMap
// 查询表内记录,封装返回为Map<属性,实体> Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks); // 查询表内记录,封装返回为Map<属性,实体>,考虑了并行流的情况 Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
E | entity | 实体对象 |
A | attribute | 实体属性类型,也是map中key的类型 |
LambdaQueryWrapper<E> | wrapper | 支持lambda的条件构造器 |
SFunction<E, A> | sFunction | 实体中属性的getter,用于封装后map中作为key的条件 |
boolean | isParallel | 为true时底层使用并行流执行 |
Consumer<E>... | peeks | 可叠加的后续操作 |
4.5.2 map
// 查询表内记录,封装返回为Map<属性,属性> Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer<E>... peeks); // 查询表内记录,封装返回为Map<属性,属性>,考虑了并行流的情况 Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer<E>... peeks);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
E | entity | 实体对象 |
A | attribute | 实体属性类型,也是map中key的类型 |
P | attribute | 实体属性类型,也是map中value的类型 |
LambdaQueryWrapper<E> | wrapper | 支持lambda的条件构造器 |
SFunction<E, A> | keyFunc | 封装后map中作为key的条件 |
SFunction<E, P> | valueFunc | 封装后map中作为value的条件 |
boolean | isParallel | 为true时底层使用并行流执行 |
Consumer<E>... | peeks | 可叠加的后续操作 |
4.5.3 group
/ 查询表内记录,封装返回为Map<属性,List<实体>> Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, A> sFunction, Consumer<T>... peeks); // 查询表内记录,封装返回为Map<属性,List<实体>>,考虑了并行流的情况 Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks); // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器> M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, Consumer<T>... peeks); // 查询表内记录,封装返回为Map<属性,分组后对集合进行的下游收集器>,考虑了并行流的情况 M group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<? super T, A, D> downstream, boolean isParallel, Consumer<T>... peeks);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
K | attribute | 实体属性类型,也是map中key的类型 |
D | - | 下游收集器返回类型,也是map中value的类型 |
A | - | 下游操作中间类型 |
M | - | 最终结束返回的Map<K, D> |
LambdaQueryWrapper<E> | wrapper | 支持lambda的条件构造器 |
SFunction<E, A> | sFunction | 分组依据,封装后map中作为key的条件 |
Collector<T, A, D> | downstream | 下游收集器 |
boolean | isParallel | 为true时底层使用并行流执行 |
Consumer<T>... | peeks | 可叠加的后续操作 |
4.5.4 list
// 查询表内记录,封装返回为List<属性> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks); // 查询表内记录,封装返回为List<属性>,考虑了并行流的情况 List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
E | entity | 实体对象 |
A | attribute | 实体属性类型,也是list中元素的类型 |
LambdaQueryWrapper<E> | wrapper | 支持lambda的条件构造器 |
SFunction<E, A> | sFunction | 封装后list中的元素 |
boolean | isParallel | 为true时底层使用并行流执行 |
Consumer<E>... | peeks | 可叠加的后续操作 |
4.6 Db 类
说明:
-
使用静态调用的方式,执行CRUD方法,避免
Spring
环境下Service
循环注入、简洁代码,提升效率 -
需要项目中已注入对应实体的BaseMapper
-
完整使用方式见: 测试用例(opens new window)
-
对于参数为Wrapper的,需要在Wrapper中传入Entity或者EntityClass供寻找对应的Mapper
-
不建议在循环中调用,如果是批量保存,建议将数据构造好后使用 Db.saveBatch(数据) 保存
例如:
// 根据id查询 List<Entity> list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class); // 根据条件构造器查询 List<Entity> list = Db.list(Wrappers.lambdaQuery(Entity.class)); // 批量根据id更新 boolean isSuccess = Db.updateBatchById(list);
五、 条件构造器
说明:
-
以下出现的第一个入参
boolean condition
表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age) -
以下代码块内的多个方法均为从上往下补全个别
boolean
类型的入参,默认为true
-
以下出现的泛型
Param
均为Wrapper
的子类实例(均具有AbstractWrapper
的所有方法) -
以下方法在入参中出现的
R
为泛型,在普通wrapper中是String
,在LambdaWrapper
中是函数(例:Entity::getId
,Entity
为实体类,getId
为字段id
的getter Method) -
以下方法入参中的
R column
均表示数据库字段,当R
具体类型为String
时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)而不是实体类数据字段名!!! -
以下举例均为使用普通wrapper,入参为
Map
和List
的均以json
形式表现! -
使用中如果入参的
Map
或者List
为空,则不会加入最后生成的sql中!!! -
有任何疑问就点开源码看,看不懂函数的点击我学习新知识(opens new window)
警告:
不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
-
wrapper 很重
-
传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
-
正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
-
我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr
5.1 AbstractWrapper
说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
5.1.1 allEq
allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
全部eq(或个别isNull)
个别参数说明:
params
: key
为数据库字段名,value
为字段值null2IsNull
: 为true
则在map
的value
为null
时调用 isNull 方法,为false
时则忽略value
为null
的
-
例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 = '老王'
allEq(BiPredicate<R, V> filter, Map<R, V> params) allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
个别参数说明:
filter
: 过滤函数,是否允许字段传入比对条件中params
与 null2IsNull
: 同上
-
例1:
allEq((k,v) -> k.contains("a"), {id:1,name:"老王",age:null})
--->name = '老王' and age is null
-
例2:
allEq((k,v) -> k.contains("a"), {id:1,name:"老王",age:null}, false)
--->name = '老王'
5.1.2 eq =
eq(R column, Object val) eq(boolean condition, R column, Object val)
-
等于 =
-
例:
eq("name", "老王")
--->name = '老王'
5.1.3 ne <>
ne(R column, Object val) ne(boolean condition, R column, Object val)
-
不等于 <>
-
例:
ne("name", "老王")
--->name <> '老王'
5.1.4 gt >
gt(R column, Object val) gt(boolean condition, R column, Object val)
-
大于 >
-
例:
gt("age", 18)
--->age > 18
5.1.5 ge >=
ge(R column, Object val) ge(boolean condition, R column, Object val)
-
大于等于 >=
-
例:
ge("age", 18)
--->age >= 18
5.1.6 lt <
lgt(R column, Object val) lt(boolean condition, R column, Object val)
-
小于 <
-
例:
lt("age", 18)
--->age < 18
5.1.7 le <=
le(R column, Object val) le(boolean condition, R column, Object val)
-
小于等于 <=
-
例:
le("age", 18)
--->age <= 18
5.1.8 between
between(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2)
-
BETWEEN 值1 AND 值2
-
例:
between("age", 18, 30)
--->age between 18 and 30
5.1.9 notBetween
notBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2)
-
NOT BETWEEN 值1 AND 值2
-
例:
notBetween("age", 18, 30)
--->age not between 18 and 30
5.1.10 like
like(R column, Object val) like(boolean condition, R column, Object val)
-
LIKE '%值%'
-
例:
like("name", "王")
--->name like '%王%'
5.1.11 notLike
notLike(R column, Object val) notLike(boolean condition, R column, Object val)
-
NOT LIKE '%值%'
-
例:
notLike("name", "王")
--->name not like '%王%'
5.1.12 likeLeft
likeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val)
-
LIKE '%值'
-
例:
likeLeft("name", "王")
--->name like '%王'
5.1.13 likeRight
likeRight(R column, Object val) likeRight(boolean condition, R column, Object val)
-
LIKE '值%'
-
例:
likeRight("name", "王")
--->name like '王%'
5.1.14 notLikeLeft
notLikeLeft(R column, Object val) notLikeLeft(boolean condition, R column, Object val)
-
NOT LIKE '%值'
-
例:
notLikeLeft("name", "王")
--->name not like '%王'
5.1.15 notLikeRight
notLikeRight(R column, Object val) notLikeRight(boolean condition, R column, Object val)
-
NOT LIKE '值%'
-
例:
notLikeRight("name", "王")
--->name not like '王%'
5.1.16 isNull
isNull(R column) isNull(boolean condition, R column)
-
字段 IS NULL
-
例:
isNull("name")
--->name is null
5.1.17 isNotNull
isNotNull(R column) isNotNull(boolean condition, R column)
-
字段 IS NOT NULL
-
例:
isNotNull("name")
--->name is not null
5.1.18 in
in(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value)
-
字段 IN (value.get(0), value.get(1), ...)
-
例:
in("age",{1,2,3})
--->age in (1,2,3)
in(R column, Object... values) in(boolean condition, R column, Object... values)
-
字段 IN (v0, v1, ...)
-
例:
in("age", 1, 2, 3)
--->age in (1,2,3)
5.1.19 notIn
notIn(R column, Collection<?> value) notIn(boolean condition, R column, Collection<?> value)
-
字段 NOT IN (value.get(0), value.get(1), ...)
-
例:
notIn("age",{1,2,3})
--->age not in (1,2,3)
notIn(R column, Object... values) notIn(boolean condition, R column, Object... values)
-
字段 NOT IN (v0, v1, ...)
-
例:
notIn("age", 1, 2, 3)
--->age not in (1,2,3)
5.1.20 inSql
inSql(R column, String inValue) 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)
5.1.21 notInSql
notInSql(R column, String inValue) 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")
--->id not in (select id from table where id < 3)
5.1.22 groupBy
groupBy(R... columns) groupBy(boolean condition, R... columns)
-
分组:GROUP BY 字段, ...
-
例:
groupBy("id", "name")
--->group by id,name
5.1.23 orderByAsc
orderByAsc(R... columns) orderByAsc(boolean condition, R... columns)
-
排序:ORDER BY 字段, ... ASC
-
例:
orderByAsc("id", "name")
--->order by id ASC,name ASC
5.1.24 orderByDesc
orderByDesc(R... columns) orderByDesc(boolean condition, R... columns)
-
排序:ORDER BY 字段, ... DESC
-
例:
orderByDesc("id", "name")
--->order by id DESC,name DESC
5.1.25 orderBy
orderBy(boolean condition, boolean isAsc, R... columns)
-
排序:ORDER BY 字段, ...
-
例:
orderBy(true, true, "id", "name")
--->order by id ASC,name ASC
5.1.26 having
having(String sqlHaving, Object... params) 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
5.1.27 func
func(Consumer<Children> consumer) func(boolean condition, Consumer<Children> consumer)
-
func 方法(主要方便在出现if...else下调用不同方法能不断链)
-
例:
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
5.1.28 or
or() or(boolean condition)
-
拼接 OR
注意事项:
主动调用or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接)
-
例:
eq("id",1).or().eq("name","老王")
--->id = 1 or name = '老王'
or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer)
-
OR 嵌套
-
例:
or(i -> i.eq("name", "李白").ne("status", "活着"))
--->or (name = '李白' and status <> '活着')
5.1.29 and
and(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer)
-
AND 嵌套
-
例:
and(i -> i.eq("name", "李白").ne("status", "活着"))
--->and (name = '李白' and status <> '活着')
5.1.30 nested
nested(Consumer<Param> consumer) nested(boolean condition, Consumer<Param> consumer)
-
正常嵌套 不带 AND 或者 OR
-
例:
nested(i -> i.eq("name", "李白").ne("status", "活着"))
--->(name = '李白' and status <> '活着')
5.1.31 apply
apply(String applySql, Object... params) apply(boolean condition, String applySql, Object... params)
-
拼接 sq
注意事项:
该方法可用于数据库函数 动态入参的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'")
5.1.32 last
last(String lastSql) last(boolean condition, String lastSql)
-
无视优化规则直接拼接到 sql 的最后
注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
-
例:
last("limit 1")
5.1.33 exists
exists(String existsSql) exists(boolean condition, String existsSql)
-
拼接 EXISTS ( sql语句 )
-
例:
exists("select id from table where age = 1")
--->exists (select id from table where age = 1)
5.1.34 notExists
notExists(String notExistsSql) 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)
5.2 QueryWrapper
说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
5.2.1 select
select(String... sqlSelect) select(Predicate<TableFieldInfo> predicate) select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
-
设置查询字段
说明:
以上方法分为两类.第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper
内的entity
属性有值! 这两类方法重复调用以最后一次为准
-
例:
select("id", "name", "age")
-
例:
select(i -> i.getProperty().startsWith("test"))
5.3 UpdateWrapper
说明:
继承自 AbstractWrapper
,自身的内部属性 entity
也用于生成 where 条件及 LambdaUpdateWrapper
, 可以通过 new UpdateWrapper().lambda()
方法获取!
5.3.1 set
set(String column, Object val) set(boolean condition, String column, Object val)
-
SQL SET 字段
-
例:
set("name", "老李头")
-
例:
set("name", "")
--->数据库字段值变为空字符串 -
例:
set("name", null)
--->数据库字段值变为null
5.3.2 setSql
setSql(String sql)
-
设置 SET 部分 SQL
-
例:
setSql("name = '老李头'")
5.3.3 lambda
-
获取
LambdaWrapper
在QueryWrapper
中是获取LambdaQueryWrapper
在UpdateWrapper
中是获取LambdaUpdateWrapper
5.4 使用 Wrapper 自定义SQL
注意事项:
需要mybatis-plus
版本 >= 3.0.7
param 参数名要么叫ew
,要么加上注解@Param(Constants.WRAPPER)
使用${ew.customSqlSegment}
不支持 Wrapper
内的entity生成where语句
5.4.1 kotlin持久化对象定义最佳实践
由于kotlin
相比于java
多了数据对象(data class
),在未说明情况下可能会混用。建议按照以下形式定义持久化对象
@TableName("sys_user") class User { @TableId(type = IdType.AUTO) var id: Int? = null @TableField("username") var name: String? = null var roleId: Int? = null }
注意:这里的TableId
及TableField
并非必要,只是为了展示Mybatis-Plus
中的annotation
使用
这里所有成员都需要定义为可空类型(?
),并赋予null
的初始值,方便我们在以下场景中使用(类似java中的updateSelective
)
val wrapper = KtUpdateWrapper(User::class.java).eq(User::id, 2) val newRecord = User() newRecord.name = "newName" userMapper!!.update(newRecord, wrapper)
不建议使用data class
及全参数构造方法,这样我们会写很多不必要的null
来构造一个空对象
5.4.2 用注解
@Select("select * from mysql_data ${ew.customSqlSegment}") List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
5.4.3 用XML
List<MysqlData> getAll(Wrapper ew);
<select id="getAll" resultType="MysqlData"> SELECT * FROM mysql_data ${ew.customSqlSegment} </select>
5.4.4 kotlin使用wrapper
kotlin 可以使用 QueryWrapper
和 UpdateWrapper
但无法使用 LambdaQueryWrapper
和 LambdaUpdateWrapper
如果想使用 lambda 方式的 wrapper 请使用 KtQueryWrapper
和 KtUpdateWrapper
val queryWrapper = KtQueryWrapper(User()).eq(User::name, "sss").eq(User::roleId, "sss2") userMapper!!.selectList(queryWrapper) val updateConditionWrapper = KtUpdateWrapper(User()).eq(User::name, "sss").eq(User::roleId, "sss2") val updateRecord = User() updateRecord.name = "newName" userMapper!!.update(updateRecord, updateConditionWrapper) val updateRecord = User() updateRecord.id = 2 updateRecord.name = "haha" userMapper.updateById(updateRecord)
5.4.5 链式调用 lambda 式
// 区分: // 链式调用 普通 UpdateChainWrapper<T> update(); // 链式调用 lambda 式。注意:不支持 Kotlin LambdaUpdateChainWrapper<T> lambdaUpdate(); // 等价示例: query().eq("id", value).one(); lambdaQuery().eq(Entity::getId, value).one(); // 等价示例: update().eq("id", value).remove(); lambdaUpdate().eq(Entity::getId, value).remove();
六、主键策略
提示
主键生成策略必须使用 INPUT
支持父类定义 @KeySequence 子类继承使用
支持主键类型指定(3.3.0 开始自动识别主键类型)
内置支持:
-
DB2KeyGenerator
-
H2KeyGenerator
-
KingbaseKeyGenerator
-
OracleKeyGenerator
-
PostgreKeyGenerator
如果内置支持不满足你的需求,可实现 IKeyGenerator 接口来进行扩展.
举个栗子
@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class) public class YourEntity { @TableId(value = "ID_STR", type = IdType.INPUT) private String idStr; }
6.1 Spring-Boot
6.1.1 方式一:使用配置类
@Bean public IKeyGenerator keyGenerator() { return new H2KeyGenerator(); }
6.1.2 方式二:通过 MybatisPlusPropertiesCustomizer 自定义
@Bean public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() { return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator()); }
6.2 Spring
6.1.1 方式一: XML 配置
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig"> <property name="dbConfig" ref="dbConfig"/> </bean> <bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig"> <property name="keyGenerator" ref="keyGenerator"/> </bean> <bean id="keyGenerator" class="com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator"/>
6.1.2 方式二:注解配置
@Bean public GlobalConfig globalConfig() { GlobalConfig conf = new GlobalConfig(); conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerator(new H2KeyGenerator())); return conf; }