什么是MybatisPlus
MybatisPlus官网MyBatis-Plus 🚀 为简化开发而生 (baomidou.com)
MyBatis-Plus是MyBatis的一个强大的增强工具包,用于简化开发。该工具包为MyBatis提供了一些高效、有用、开箱即用的功能,使用它可以有效地节省您的开发时间。
开始
一、添加依赖
Mybaits官方提供了starter,集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果,
项目中只需引入MybatisPlus依赖即可
Maven
Springboot2
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>Latest Version</version>
</dependency>
Springboot3
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>Latest Version</version>
</dependency>
MybaitsPlus只做增强不做改变,引入不会对现有工程造成影响,简单配置即可实现对单表的CURD操作,节省大量时间
二、继承BaseMapper接口
泛型指定对应的实体类型
public interface UserMapper extends BaseMapper<User> {
}
BaseMapper中定义了增删改查方法
三、使用
增
userMapper.insert(user);
删
userMapper.deleteById(5L);
改 只会修改填入的字段
User user = new User();
user.setId(5L);
user.setBalance(20000);
//只修改填入的字段
userMapper.updateById(user);
执行的sql语句
UPDATE user SET balance = ? WHERE id = ?
查
User user = userMapper.selectById(5L);
批量操作,使用对应的Batch方法即可
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
常用注解配置
@TableName
该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致,或者实体类名不是数据库表名的驼峰写法时,您需要使用这个注解来明确指定表名。
@TableName("sys_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
value
类型: String
默认值: ""
指定实体类对应的数据库表名。如果实体类名与表名不一致,使用这个属性来指定正确的表名。
resultMap
类型: String
默认值: ""
指定在 XML 中定义的 ResultMap 的 ID,用于将查询结果映射到特定类型的实体类对象。
resultMap的认识可以去看这位大佬的文章
浅谈MyBatis中的resultMap(个人总结)_mybatis resultmap 泛型-CSDN博客
autoResultMap
类型: boolean
默认值: false
是否自动构建 resultMap。如果已经设置了 resultMap,这个属性不会生效。
excludeProperty
类型: String[]
默认值: {}
添加于: @since 3.3.1
指定在映射时需要排除的属性名。这些属性将不会被包含在生成的 SQL 语句中。
@TableId
该注解用于标记实体类中的主键字段。如果你的主键字段名为 id,你可以省略这个注解
@TableName("sys_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
}
value
类型: String
默认值: ""
指定数据库表的主键字段名。如果不设置,MyBatis-Plus 将使用实体类中的字段名作为数据库表的主键字段名。
type
类型: Enum
默认值: IdType.NONE
指定主键的生成策略。
IdType 枚举类型定义
IdType.AUTO
:使用数据库自增 ID 作为主键。IdType.NONE
:无特定生成策略,如果全局配置中有 IdType 相关的配置,则会跟随全局配置。IdType.INPUT
:在插入数据前,由用户自行设置主键值。IdType.ASSIGN_ID
:自动分配ID
,适用于Long
、Integer
、String
类型的主键。默认使用雪花算法通过IdentifierGenerator
的nextId
实现。@since 3.3.0IdType.ASSIGN_UUID
:自动分配UUID
,适用于String
类型的主键。默认实现为IdentifierGenerator
的nextUUID
方法。@since 3.3.0
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@TableField
该注解用于标记实体类中的非主键字段,它告诉 MyBatis-Plus 如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则,并且与数据库表字段名一致,你可以省略这个注解。
使用该注解的常见场景
- 成员变量名与数据库字段名不一致
-
成员变量名以is开头且为布尔值,使用该注解指定
-
成员变量名与数据库关键字冲突 eg: order
@TableField("`order`")
private Integer order;
-
成员变量名不是数据库字段 @TableField(exist = false)
value
类型: String
默认值: ""
指定数据库中的字段名。如果你的实体类字段名与数据库字段名不同,使用这个属性来指定正确的数据库字段名。
exist
类型: boolean
默认值: true
指示这个字段是否存在于数据库表中。如果设置为 false,MyBatis-Plus 在生成 SQL 时会忽略这个字段。
typeHandler
类型: Class<? extends TypeHandler>
默认值: UnknownTypeHandler.class
类型处理器,用于指定在数据库操作中如何处理特定字段的值。这个属性允许您自定义字段值的转换逻辑,以适应特定的数据类型或业务需求。
详细说明
-
当 typeHandler 属性未设置(即使用默认值 UnknownTypeHandler.class)时,MyBatis-Plus 将使用默认的类型处理器来处理字段值。
-
当 typeHandler 属性设置为特定的 TypeHandler 子类时,该字段将使用指定的类型处理器进行数据库操作。这可以用于处理自定义类型、特殊数据格式或非标准的数据库类型。
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
此外还有诸多注解,详见官方文档
条件构造器
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
AbstractWrapper
这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
QueryWrapper
查询名字中带“o”、存款大于等于1000元的人的id、username、info、balance字段
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id","username","info","balance")
.like("username","o")
.ge("balance",1000);
List<User> users = userMapper.selectList(wrapper);
更改jack的余额
User user = new User();
user.setBalance(1699);
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username","jack");
userMapper.update(user,wrapper);
UpdateWrapper
使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
扣除多个用户余额200
List<Long> ids = List.of(1L,2L,3L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id",ids);
userMapper.update(wrapper);
LambdaQueryWrapper
这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
采用反射机制,查询名字中带“o”且余额>=1000的用户的id、username、userInfo、balance字段
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.select(User::getId,User::getUsername,User::getInfo,User::getBalance)
.like(User::getUsername,"o")
.ge(User::getBalance,1000);
List<User> users = userMapper.selectList(wrapper);
LambdaUpdateWrapper
类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
自定义SQL
利用MybatisPlus中的Wrapper构建自定义的where条件,自定义SQL语句中的剩余部分
构建条件,传入自定义方法并调用
List<Long> ids = List.of(1L,2L,4L);
int amount = 200;
//定义条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("id",ids);
//调用自定义方法
userMapper.updateBalanceByIds(wrapper,amount);
在mapper方法参数中用@Param注解声明Wrapper变量名称
//void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper,@Param("amount") int amount) 一定是 ew ,也可以用下面的常量类
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);
自定义sql,使用Wrapper条件
<update id="updateBalanceByIds">
update user
set balance = balance - #{amount}
${ew.customSqlSegment}
</update>
Service接口
基础使用
IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。
Service接口继承Iservice接口
public interface IUserService extends IService<User> {
}
Service接口实现类继承ServiceImpl<M extends BaseMapper<T>, T>
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService{
}
IService 接口中的方法命名遵循了一定的规范,如 get 用于查询单行,remove 用于删除,list 用于查询集合,page 用于分页查询,这样可以避免与 Mapper 层的方法混淆。
当IService接口中的方法不足以满足需求时,需自定义方法,在Service实现类中调用mapper时无需再次注入,使用继承而来的baseMapper(其实就是UserMapper)即可
Lambda方法
lambdaQuery
查询满足条件的用户
使用list方法需new Wrapper
使用lambdaQuery直接指定条件即可
其中第一个参数为条件,满足条件执行判断
public List<User> queryUsers(UserQuery userQuery) {
return lambdaQuery()
.like(userQuery.getUsername() != null, User::getUsername, userQuery.getUsername())
.eq(userQuery.getStatus() != null, User::getStatus, userQuery.getStatus())
.ge(userQuery.getMinBalance() != null, User::getBalance, userQuery.getMinBalance())
.le(userQuery.getMaxBalance() != null, User::getBalance, userQuery.getMaxBalance())
.list();
}
lambdaUpdate
扣减用户余额,当余额为0时冻结
lambdaUpdate()
.set(User::getBalance,user.getBalance()-money)
.set(user.getBalance()-money==0,User::getStatus,UserStatus.FROZEN)
.eq(User::getId,id)
.eq(User::getBalance,user.getBalance())//乐观锁
.update();//真正执行update
批量新增
for循环一条一条新增(速度极差,不推荐)
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
userService.save(buildUser(i));
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
IService的批量插入(基于预编译的批处理,性能较好,与上面不同是上面每一条都发起一次网络请求然后插入一条,该方法每1000条发起一次请求然后执行一千条sql语句)
List<User> list = new ArrayList<>(1000);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
list.add(buildUser(i));
if (i%1000==0){
userService.saveBatch(list);
list.clear();
}
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
mysql开启rewriteBatchedStatements=true参数
这种情况下在执行批量操作时会将多条sql语句重写成一条sql语句,性能得到极大提升
代码生成器
下载该插件
配置数据库信息
生成代码
Db Kit
Db Kit 是 Mybatis-Plus 提供的一个工具类,它允许开发者通过静态调用的方式执行 CRUD 操作,从而避免了在 Spring 环境下可能出现的 Service 循环注入问题,简化了代码,提升了开发效率。
与IService不同的是,当参数为 Wrapper 时,需要在 Wrapper 中传入 Entity 或者 EntityClass,以便寻找对应的 Mapper。
// 假设有一个 User 实体类和对应的 BaseMapper
// 根据 id 查询单个实体
User user = Db.getById(1L, User.class);
// 根据 id 查询多个实体
List<User> userList = Db.listByIds(Arrays.asList(1L, 2L, 3L), User.class);
// 根据条件构造器查询
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class)
.eq(User::getStatus, "active");
List<User> activeUsers = Db.list(queryWrapper);
// 插入新实体
User newUser = new User();
newUser.setUsername("newUser");
newUser.setAge(25);
boolean isInserted = Db.insert(newUser);
// 根据 id 更新实体
User updateUser = new User();
updateUser.setId(1L);
updateUser.setUsername("updatedUser");
boolean isUpdated = Db.updateById(updateUser);
// 根据条件构造器更新
LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate(User.class)
.set(User::getAge, 30)
.eq(User::getUsername, "updatedUser");
boolean isUpdatedByWrapper = Db.update(null, updateWrapper);
// 根据 id 删除实体
boolean isDeleted = Db.removeById(1L);
// 根据条件构造器删除
LambdaDeleteWrapper<User> deleteWrapper = Wrappers.lambdaDelete(User.class)
.eq(User::getStatus, "inactive");
boolean isDeletedByWrapper = Db.remove(deleteWrapper);
// 批量插入
List<User> batchUsers = Arrays.asList(
new User("user1", 20),
new User("user2", 22),
new User("user3", 24)
);
boolean isBatchInserted = Db.saveBatch(batchUsers);
// 批量更新
List<User> batchUpdateUsers = Arrays.asList(
new User(1L, "user1", 21),
new User(2L, "user2", 23),
new User(3L, "user3", 25)
);
boolean isBatchUpdated = Db.updateBatchById(batchUpdateUsers);
逻辑删除
Mybatis提供了逻辑删除功能,无需改变方法调用方式,在底层自动修改CURD的语句,只需在application.yaml文件中配置逻辑删除的字段和值即可
mybatis-plus:
global-config:
db-config:
# 逻辑删除字段
logic-delete-field: deleted
# 逻辑删除的值 默认是1
logic-delete-value: 1
# 逻辑未删除的值 默认是0
logic-not-delete-value: 0
自动映射枚举
基本使用
枚举属性使用 @EnumValue
注解,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。
方式一、注解标记
枚举属性使用 @EnumValue
注解,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。
@Getter
@AllArgsConstructor
public enum GradeEnum {
PRIMARY(1, "小学"),
SECONDARY(2, "中学"),
HIGH(3, "高中");
@EnumValue // 标记数据库存的值是code
private final int code;
// 其他属性...
}
方式二、实现接口
实现 IEnum
接口,实现 getValue
方法,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。
@Getter
@AllArgsConstructor
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private final int value;
private final String desc;
@Override
public Integer getValue() {
return this.value;
}
}
之后修改全局 defaultEnumTypeHandler
mybatis-plus:
configuration:
default-enum-type-handler: xx.xx.xx.MyEnumTypeHandler
如何将序列化枚举值作为前端返回值
Jackson
一、重写toString方法
Spring Boot
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
}
Jackson 独立使用
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
在枚举中重写 toString 方法,以上两种方式任选其一。
二、注解处理
public enum GradeEnum {
PRIMARY(1, "小学"),
SECONDORY(2, "中学"),
HIGH(3, "高中");
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue
@JsonValue // 标记响应json值
private final int code;
}
Fastjson
重写toString方法
全局处理
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);
局部处理
@JSONField(serialzeFeatures = SerializerFeature.WriteEnumUsingToString)
private UserStatus status;
Json处理器
MyBatis-Plus 内置了多种 JSON 类型处理器,包括 AbstractJsonTypeHandler
及其子类 Fastjson2TypeHandler
、FastjsonTypeHandler
、GsonTypeHandler
、JacksonTypeHandler
等。这些处理器可以将 JSON 字符串与 Java 对象相互转换。
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 必须开启映射注解
*
* @TableName(autoResultMap = true)
*
* 选择对应的 JSON 处理器,并确保存在对应的 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// 或者使用 FastjsonTypeHandler
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}