MyBatis Plus 基础
本文将介绍 MyBatis Plus 的基本用法。
一、注解
具体请看:
- @TableName:用于描述实体类对应的表名
- @TableId:用于描述主键
- @TableField:用于描述字段
- @OrderBy:用于指定排序规则
二、Mapper 增删改查
1. Insert
插入一条数据:
1 |
|
2. Delete
根据 ID,删除记录:
1 |
|
根据 ID 集合,批量删除记录:
1 |
|
根据 Map,删除记录:
Map 中,key 为字段名、value 为字段值,将会删除所有与 Map 相符合的记录。
1 |
|
根据条件构造器,删除记录:
1 |
|
3. Update
根据实体类中的 ID,更新记录:
1 |
|
根据条件构造器,更新记录:
根据 wrapper 匹配记录,将所有匹配到的记录根据 entity 更新。
1 |
|
4. Select
根据 ID,查询一条记录:
1 |
|
根据条件构造器,查询一条记录:
1 |
|
根据 ID 集合,查询所有记录:
1 |
|
根据 Map,查询所有记录:
Map 中,key 为字段名、value 为字段值,将会查询所有与 Map 相符合的记录。
1 |
|
根据条件构造器,查询所有记录:
将查询结果中的每一行封装进对象之中,以对象集合的形式返回。
1 |
|
根据条件构造器,查询所有记录,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 集合的形式返回。
1 |
|
根据条件构造器,查询所有记录,以第一个字段的集合返回:
只获取查询结果中每一行的第一个字段,以集合的形式返回。
1 |
|
根据分页条件和条件构造器,查询所有记录:
将查询结果中的每一行封装进对象之中。
1 |
|
根据分页条件和条件构造器,查询所有记录,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中。
1 |
|
根据条件构造器,查询符合条件的记录数:
1 |
|
三、Service 增删改查
1. Save
插入一条记录:
1 |
|
插入多条记录:
默认每批最多提交 1000 条。
1 |
|
插入多条记录:
指定每批提交的个数。
1 |
|
2. SaveOrUpdate
插入或更新记录:
若实体类中的 id 在数据库中没有对应记录,则插入记录,否则更新记录。
1 |
|
更新或插入或更新记录:
首先根据条件构造器尝试更新记录;
如果更新不成功,则尝试插入或更新记录。
1 |
|
源码如下:
1
2
3
default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
return update(entity, updateWrapper) || saveOrUpdate(entity);
}
3. Remove
根据 ID,删除记录:
1 |
|
根据 ID 集合,批量删除记录:
1 |
|
根据 Map,删除记录:
Map 中,key 为字段名、value 为字段值,将会删除所有与 Map 相符合的记录。
1 |
|
根据条件构造器,删除记录:
1 |
|
4. Update
根据实体类中的 ID,更新记录:
1 |
|
根据实体类中的 ID,批量更新记录:
默认每批最多提交 1000 条。
1 |
|
根据实体类中的 ID,批量更新记录:
指定每批提交的个数。
1 |
|
根据条件构造器,更新记录:
根据 wrapper 匹配记录,将所有匹配到的记录根据 entity 更新。
1 |
|
根据条件构造器,更新记录:
wrapper 需要设置 sqlset;
根据 wrapper 匹配记录,并根据 wrapper 更新。
1 |
|
5. Get
根据 ID,查询一条记录:
1 |
|
根据条件构造器,查询一条记录:
如果查询到多条记录将会抛出异常。
1 |
|
根据条件构造器,查询一条记录:
throwEx 用于指定如果查询到多条记录时是否抛出异常。
1 |
|
根据条件构造器,查询一条记录,以 Map 形式返回:
将查询结果(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 形式返回。
1 |
|
根据条件构造器,查询一条记录,返回为指定类型:
其中,
- V:结果的类型
- mapper:转换函数,将结果转换为 V 类型的数据
1 |
|
6. List
查询所有记录:
1 |
|
根据 ID 集合,查询所有记录:
1 |
|
根据条件构造器,查询所有记录:
1 |
|
根据 Map,查询所有记录:
Map 中,key 为字段名、value 为字段值,将会查询所有与 Map 相符合的记录。
1 |
|
查询所有记录,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 集合的形式返回。
1 |
|
根据条件构造器,查询所有记录,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 集合的形式返回。
1 |
|
查询所有记录:
1 |
|
源码:
1
2
3
default List<Object> listObjs() {
return listObjs(Function.identity());
}其中,Function.identity() 为:
1
2
3
static <T> Function<T, T> identity() {
return t -> t;
}它将始终返回其输入。
查询所有记录,返回为指定类型:
其中,
- V:结果的类型
- mapper:转换函数,将结果转换为 V 类型的数据
1 |
|
根据条件构造器,查询所有记录:
1 |
|
源码:
1
2
3
default List<Object> listObjs(Wrapper<T> queryWrapper) {
return listObjs(queryWrapper, Function.identity());
}其中,Function.identity() 为:
1
2
3
static <T> Function<T, T> identity() {
return t -> t;
}它将始终返回其输入。
根据条件构造器,查询所有记录:
其中,
- V:结果的类型
- mapper:转换函数,将结果转换为 V 类型的数据
1 |
|
7. Page
分页查询:
1 |
|
根据条件构造器,分页查询:
1 |
|
分页查询,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 集合的形式返回。
1 |
|
根据条件构造器,分页查询,以 Map 形式返回:
将查询结果中的每一行(以 key 为字段名、value 为字段值的方式)封装进 Map 中,以 Map 集合的形式返回。
1 |
|
8. Count
查询总记录数:
1 |
|
根据条件构造器,查询总记录数:
1 |
|
9. 链式查询
1 |
|
1
2
3
// 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
10. 链式修改
1 |
|
1
2
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
四、条件构造器
具体请看:
boolean condition
:该参数表示条件是否生效。当 condition 为 false 时,该条件将不会出现在 SQL 中。
可以在此处加入判断,从而实现动态增减条件
类型为 boolean 的参数可以不出现,不出现时默认为 true
BiPredicate 是一个函数结果,它
1. allEq - 全等
1 |
|
Map<字段名, 字段值> params
:以 key 为字段名、value 为字段值,描述记录应该符合的状态boolean null2IsNull
:- 为 true 时,当 map 中某个键值对的值为 null,对应的 SQL 将为
键 is null
- 为 false 时,当 map 中某个键值对的值为 null,将不会有对应的 SQL
- 为 true 时,当 map 中某个键值对的值为 null,对应的 SQL 将为
allEq({id:1,name:”老王”,age:null}) —> id = 1 and name = ‘老王’ and age is null
allEq({id:1,name:”老王”,age:null}, false) —> id = 1 and name = ‘老王’
1 |
|
BiPredicate<字段名, 字段值> filter
:过滤函数,根据字段名和字段值进行判断,根据判断结果确定字段对应的条件是否生效BiPredicate 是一个函数接口,它接收两个参数,并返回一个 boolean 值
Map<字段名, 字段值> params
:以 key 为字段名、value 为字段值,描述记录应该符合的状态boolean null2IsNull
:- 为 true 时,当 map 中某个键值对的值为 null,对应的 SQL 将为
键 is null
- 为 false 时,当 map 中某个键值对的值为 null,将不会有对应的 SQL
- 为 true 时,当 map 中某个键值对的值为 null,对应的 SQL 将为
allEq((k,v) -> k.indexOf(“a”) >= 0, {id:1,name:”老王”,age:null}) —> name = ‘老王’ and age is null
allEq((k,v) -> k.indexOf(“a”) >= 0, {id:1,name:”老王”,age:null}, false) —> name = ‘老王’
2. eq - 等于
1 |
|
eq(“name”, “老王”) —> name = ‘老王’
3. ne - 不等于
1 |
|
ne(“name”, “老王”) —> name <> ‘老王’
4. gt - 大于
1 |
|
gt(“age”, 18) —> age > 18
5. ge - 大于等于
1 |
|
ge(“age”, 18) —> age >= 18
6. lt - 小于
1 |
|
lt(“age”, 18) —> age < 18
7. le - 小于等于
1 |
|
le(“age”, 18) —> age <= 18
8. between - 之间
1 |
|
between(“age”, 18, 30) —> age between 18 and 30
9. notBetween - 不在之间
1 |
|
notBetween(“age”, 18, 30) —> age not between 18 and 30
10. like
1 |
|
like(“name”, “王”) —> name like ‘%王%’
11. notLike
1 |
|
notLike(“name”, “王”) —> name not like ‘%王%’
12. likeLeft
1 |
|
likeLeft(“name”, “王”) —> name like ‘%王’
13. likeRight
1 |
|
likeRight(“name”, “王”) —> name like ‘王%’
14. isNull
1 |
|
isNull(“name”) —> name is null
15. isNotNull
1 |
|
isNotNull(“name”) —> name is not null
16. in
1 |
|
in(“age”,{1,2,3}) —> age in (1,2,3)
1 |
|
in(“age”, 1, 2, 3) —> age in (1,2,3)
17. notIn
1 |
|
notIn(“age”,{1,2,3}) —> age not in (1,2,3)
1 |
|
notIn(“age”, 1, 2, 3) —> age not in (1,2,3)
18. inSql
1 |
|
- inValue:SQL 语句,将会被直接拼接至 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)
19. notInSql
1 |
|
- inValue:SQL 语句,将会被直接拼接至 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)
20. groupBy
1 |
|
groupBy(“id”, “name”) —> group by id,name
21. orderByAsc - 升序排序
1 |
|
orderByAsc(“id”, “name”) —> order by id ASC,name ASC
22. orderByDesc - 降序排序
1 |
|
orderByDesc(“id”, “name”) —> order by id DESC,name DESC
23. orderBy
1 |
|
orderBy(true, true, “id”, “name”) —> order by id ASC,name ASC
24. having
1 |
|
having(“sum(age) > 10”) —> having sum(age) > 10
having(“sum(age) > {0}”, 11) —> having sum(age) > 11
25. func - 根据条件调用不同的方法
1 |
|
consumer:函数,根据条件调用不同的方法
Consumer 是一个函数式接口,表示接收一个参数且没有返回值的函数
func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)})
26. or
1 |
|
or()
表示下一个方法是用 or 连接的(默认为 and 连接)。
or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> or (name = ‘李白’ and status <> ‘活着’)
27. and
1 |
|
and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> and (name = ‘李白’ and status <> ‘活着’)
28. nested
1 |
|
正常嵌套,不带 and 或 or
nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> (name = ‘李白’ and status <> ‘活着’)
29. apply
1 |
|
拼接 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’”)
30. last
1 |
|
在 SQL 的最后后拼接语句
31. exists
1 |
|
用 SQL 拼接 exists
exists(“select id from table where age = 1”) —> exists (select id from table where age = 1)
32. notExists
1 |
|
用 SQL 拼接 not exists
notExists(“select id from table where age = 1”) —> not exists (select id from table where age = 1)
33. select
仅适用于 QueryWrapper
1 |
|
select(“id”, “name”, “age”)
1 |
|
entityClass
:实体类的 classPredicate<TableFieldInfo> predicate
:过滤查询字段
1 |
|
entityClass
:实体类的 classPredicate<TableFieldInfo> predicate
:过滤查询字段- 用此方法时,需要保证 wrapper 中的 entity 属性有值
select(i -> i.getProperty().startsWith(“test”))
34. set
仅适用于 UpdateWrapper
1 |
|
set(“name”, “老李头”)
35. setSql
仅适用于 UpdateWrapper
1 |
|
setSql(“name = ‘老李头’”)
36. lambda
仅适用于 QueryWrapper 和 UpdateWrapper
- 在 QueryWrapper 中是获取 LambdaQueryWrapper
- 在 UpdateWrapper 中是获取 LambdaUpdateWrapper
37. 自定义 SQL - 注解
如果希望自定义 SQL,则方法的参数应该为:
Wrapper ew
1
2
3
* ```java
@Param(Constants.WRAPPER) Wrapper 任意名字
(1) 注解
1 |
|
(2) XML
1 |
|
1 |
|
38. Lambda
1 |
|
等价于:
1 |
|
五、分页插件
1. 配置 - SpringBoot 方式
新建 MybatisPlus 配置类,增加方法如下:
1 |
|
- @Configuration:对类注解,用于将类标记为自定义配置类
- @Bean:对方法注解,方法将会产生一个 Bean,并交由 SpringBoot 管理
2. 自带的分页方法
实例化 Page 对象,并传入将分页选项以参数方式传入
1
Page<User> page = new Page<>(当前页数,每页条数);
调用分页查询方法,将 Page 对象传入,获取查询后返回的 Page 对象
通过 Page 对象的
getRecords()
方法,获取查询出来的数据
1 |
|
3. 自定义方法的分页
如果自定义方法也能实现分页效果,只需要将自定义方法的返回值设为 Page,并在参数的首位新增 Page 对象即可。
1
2
3
4
5
6
7
8
9
10
11
public interface UserMapper {//可以继承或者不继承BaseMapper
/**
* 查询
*
* @param page 分页对象,必须放在第一位
* @param state 状态
* @return 分页对象
*/
Page<User> select(Page<?> page, Integer state);
}
1
2
3
<select id="select" resultType="全限定类名">
SELECT id,name FROM user WHERE state=#{state}
</select>
六、主键生成
1. 生成方式
MyBatis Plus 默认使用雪花算法生成主键 ID,主键 ID 类型为 Long - BIGINT 或 String - VARCHAR。
snowflake 算法是 Twitter 开源的分布式 ID 生成算法,结果是一个 long 类型的 ID 。
其核心思想:使用 41bit 作为毫秒数,10bit 作为机器的 ID(5bit 数据中心,5bit 机器ID),12bit 作为毫秒内的流水号(意味着每个节点在每个毫秒可以产生 4096 个ID),最后还有一个符号位,永远是0。
2. 主键策略
MyBatis Plus 默认有 5 种主键生成策略。
值 | 描述 |
---|---|
AUTO | 由数据库自增生成 ID |
NONE | 默认,会跟随全局 |
INPUT | 自行输入 ID,或由填充插件生成 |
ASSIGN_ID | 在插入数据库之前生成 ID,主键类型为 Number 或 String |
ASSIGN_UUID | 在插入数据库之前生成 UUID,主键类型为 String |
当主键生成策略为 INPUT、ASSIGN_ID、ASSIGN_UUID
- 如果设置主键策略为 Auto,当数据库支持主键递增且配置了主键递增,ID 将交由数据库,通过数据库自增的方式生成
- 如果设置主键策略为 NONE 或不设置主键策略,则默认会使用 ASSIGN_ID 策略
- 如果设置主键策略为 INPUT,则代表自行输入 ID,也可以通过”序列生成”
- 如果设置主键策略为 ASSIGN_ID 和 ASSIGN_UUID,Mybatis Plus 将会使用雪花算法来自动生成 ID
3. 主键策略局部配置
在实体类中为主键注解 @TableId(type = 策略名)
来进行指定。
4. 主键策略全局配置
在 SpringBoot 配置文件中添加配置信息如下:
1 |
|
5. 主键策略 - INPUT
如果设置主键策略为 INPUT,则代表自行输入 ID,也可以通过”序列生成”
(1) 自行输入
如果自行数据,则传递给插入方法的实体对象应该自带 ID。
(2) 序列生成
对于某些有序列的数据库(如:Oracle、SQLServer 等),可以通过序列生成主键。
Mybatis Plus 支持 DB2、H2、Kingbase、Oracle 和 Postgre 数据库的序列生成
具体步骤如下:
在数据库中新建序列
配置序列生成插件
法 1:在 MyBatis Plus 配置类中配置
1
2
3
4@Bean
public IKeyGenerator keyGenerator() {
return new OracleKeyGenerator();
}法 2:使用 Mybatis Plus PropertiesCustomizer 自定义
1
2
3
4@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new OracleKeyGenerator());
}
配置实体类:
- 为类增加注解
@KeySequence(value = 序列名)
- 为主键增加注解
@TableId(type = IdType.INPUT)
1
2
3
4
5
6
7@KeySequence(value = 序列名)
public class User {
@TableId(type = IdType.INPUT)
private String id;
}- 为类增加注解
6. 自定义主键生成器
(1) 定义主键生成器类
新建类,实现 IdentifierGenerator 接口:
1 |
|
(2) 配置主键生成器
法 1:为主键生成器类加上注解,声明为 Bean,以便 Spring 扫描注入
法 2:在 MyBatis Plus 配置类中新增
idGenerator()
方法,使方法返回主键生成器类的实例,并使用@Bean
注解1
2
3
4@Bean
public IdentifierGenerator idGenerator() {
return new MyIdGenerator();
}法 3:使用 Mybatis Plus PropertiesCustomizer 自定义
1
2
3
4@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new MyGenerator());
}