说明
教程
视频教程:https://www.bilibili.com/video/BV1Xu411A7tL
详细文档:https://b11et3un53m.feishu.cn/wiki/PsyawI04ei2FQykqfcPcmd7Dnsc
MyBatisPlus作用
MyBatisPlus只需要简单的配置,即可快速进行单表的增删改查。还具有代码生成、自动分页、逻辑删除、自动填充等扩展功能。
注意事项
- MyBatisPlus擅长的是单表的增删改查,如果多表的查询或者sql语句比较复杂,是需要手写sql语句的。
- 更新操作,默认只更新非null的数据(更新策略可以在配置中修改)。
常见注解
@MapperScan
扫描mapper的注解,配置要扫描的包路径,扫描到的mapper才能够生效。
实体类上的注解
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:
- 把PO实体的类名驼峰转下划线作为表名
- 把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
- 把名为id的字段作为主键
@TableName
表名注解,标识实体类对应的表
@TableId
主键注解,标识实体类中的主键字段
两个属性value和type,其中type的常用类型
- AUTO:利用数据库的id自增长
- INPUT:手动生成id
- ASSIGN_ID:雪花算法生成Long类型的全局唯一id,这是默认的ID策略
@TableField
普通字段注解
一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:
- 成员变量名与数据库字段名不一致
- 成员变量是以is开头且是布尔值,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
- 成员变量名与数据库的关键字冲突。使用@TableField注解给字段名添加````转义
- 变量不是数据表里的字段
示例:
@TableName("tb_user")
public class User {
//自增长
@TableId(type = IdType.AUTO)
private Long id;
//变量名与数据表字段名不一致
@TableField("user_name")
private String name;
private Integer age;
//成员变量是以is开头且是布尔值
@TableField("is_married")
private Boolean isMarried;
//成员变量名与数据库的关键字冲突
@TableField("`order`")
private String order;
//变量不是数据表里的字段
@TableField(exist = false)
private String info;
}
常见配置
MybatisPlus也支持基于yaml文件的自定义配置
官方文档:https://www.baomidou.com/reference/
示例:
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包。当mapper.xml中定义实体类类型,就不用定义全路径名了
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址。当前这个是默认值,可以不写,如果和这个不一样需要自定义
configuration:
map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射,默认开启
cache-enabled: false # 是否开启二级缓存,默认开启
global-config:
db-config:
id-type: assign_id # id为雪花算法生成,设为auto就是自增长
update-strategy: not_null # 默认更新策略:只更新非空字段
大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值或者需要修改的,例如:
- 实体类的别名扫描包
- 全局id类型(默认是雪花算法)
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto # 全局id类型为自增长
条件构造器
Wrapper(条件构造器)
继承关系:

Wrapper
AbstractWrapper:提供了where中包含的所有条件构造方法(eq、gt、lt、like等)
- QueryWrapper:拓展了一个select方法,允许指定查询字段
- UpdateWrapper:拓展了一个set方法,允许指定SQL中的SET部分
- AbstractLambdaWrapper:避免写死字段名,基于Lambda表达式
- LambdaUpdateWrapper
- LambdaQueryWrapper
条件构造器的使用选择:
- QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
- UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
- 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
QueryWrapper示例:

//查询名字里带o并且balance大于1000的
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> userList = userMapper.selectList(queryWrapper);
构造器常用方法:
- eq 就是 equal等于
- ne 就是 not equal不等于
- gt 就是 greater than大于
- lt 就是 less than小于
- ge 就是 greater than or equal 大于等于
- le 就是 less than or equal 小于等于
- in 就是 in 包含(数组)
- isnull 就是 等于null
- between 就是 在2个条件之间(包括边界值)
- like就是 模糊查询
UpdateWrapper示例:
UPDATE user
SET balance = balance + 200
WHERE id in (1, 2, 4)
//更新id符合条件的行中balance加200
List<Long> ids = new ArrayList<>();
Collections.addAll(ids, 1L, 2L, 4L);
UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>()
.setSql("balance = balance + 200")
.in("id", ids);
userMapper.update(null, updateWrapper);
LambdaQueryWrapper基于Lambda表达式,使用getter方法结合反射技术获取变量名,可以避免硬编码。
示例:
//查询名字里带o并且balance大于1000的
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
List<User> userList = userMapper.selectList(queryWrapper);
自定义sql
上面的更新示例中,把sql语句的一部分写到业务代码中了,这种写法在一些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。
所以,MybatisPlus让我们可以利用Wrapper生成查询条件,再结合Mapper.xml编写SQL语句剩下的部分。
业务代码基于Wrapper构建where条件
// 1. 更新条件
List<Long> ids = new ArrayList<>();
Collections.addAll(ids, 1L, 2L, 4L);
int amount = 200;
// 2. 定义方法
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
.in(User::getId, ids);
// 3. 调用自定义sql方法
userMapper.updateBalanceByIds(wrapper, 200);
在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew,可以写成 "ew" 或者 Constants.WRAPPER
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaUpdateWrapper<User> wrapper, @Param("amount") int amount);
}
在xml文件中编写sql,并使用传入的Wrapper条件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.user.mapper.UserMapper">
<update id="updateBalanceByIds">
UPDATE user SET balance = balance + #{amount} ${ew.customSqlSegment}
</update>
</mapper>
IService接口

IService的 Lambda 方法
Service 中对 LambdaQueryWrapper 和 LambdaUpdateWrapper 的用法进一步做了简化。我们无需自己通过 new 的方式来创建 Wrapper,而是直接调用 lambdaQuery() 和 lambdaUpdate() 方法:
lambdaQuery() 示例:
@Override
public List<UserVO> queryUsers(String username, Integer status, Integer minBalance, Integer maxBalance) {
// 查询用户
List<User> users = lambdaQuery()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
// 处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
lambdaQuery() 方法中除了可以构建条件,还需要在链式编程的最后添加一个 list(),这是在告诉MyBatisPlus 我们的调用结果需要是一个 list 集合。这里不仅可以用 list(),可选的方法有:
- one():最多1个结果
- list():返回集合结果
- count():返回计数结果
MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果。
lambdaUpdate() 示例:
// 扣减余额 update tb_user set balance = balance - ?
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance, remainBalance) // 更新余额
.set(remainBalance == 0, User::getStatus, 2) // 动态判断,是否更新status
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance()) // 乐观锁
.update();
修改余额的操作为了防止并发问题,加上了乐观锁,判断当前的余额和之前查到的余额是否一致。
视频教程:https://www.bilibili.com/video/BV1S142197x7?p=11
批量新增
// 准备10万条数据
List<User> list = new ArrayList<>(1000);
for (int i = 1; i <= 100000; i++) {
list.add(buildUser(i));
// 每1000条批量插入一次
if (i % 1000 == 0) {
userService.saveBatch(list);
list.clear();
}
}
要想将多条插入SQL合并为一条,还需要在 jdbc 的 url 后面添加参数 &rewriteBatchedStatements=true
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: MySQL123
https://www.bilibili.com/video/BV1S142197x7/?p=12
扩展功能
代码生成器
推荐使用一款 MybatisPlus 的插件,它可以基于图形化界面完成 MybatisPlus 的代码生成,非常简单。


新版idea好像不能在导航栏增加 “other” 选项卡了,生成代码的两个选项迁移到"tools"选项卡了。
使用:
点击Idea顶部菜单中的other,然后选择 Code Generator

https://www.bilibili.com/video/BV1S142197x7?p=14
Db静态工具
有的时候 Service 之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个Db静态工具类。
// 利用Db实现复杂条件查询
List<User> list = Db.lambdaQuery(User.class)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000)
.list();
逻辑删除
MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名,字段类型可以是boolean、integer
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
逻辑删除本身也有自己的问题,比如:
- 会导致数据库表垃圾数据越来越多,影响查询效率
- SQL中全都需要对逻辑删除字段做判断,影响查询效率
因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。
枚举处理器
MybatisPlus提供了一个处理枚举的类型转换器,可以帮我们把枚举类型与数据库类型自动转换。
@Getter
public enum UserStatus {
NORMAL(1, "正常"),
FREEZE(2, "冻结")
;
@EnumValue //此注解指定存入数据库的值
private final int value;
@JsonValue //返回类型可以通过此注解指定
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
配置:
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
JSON类型处理器
MySQL 5.7 版本开始支持JSON 对象和JSON 数组两种类型。
JSON 类型比较适合存储一些列不固定、修改较少、相对静态的数据,比如用户画像、商品标签、接口数据等
MySQL JSON数据类型
2068

被折叠的 条评论
为什么被折叠?



