核心功能
P5 条件构造器
mybatisplus支持各种复杂的where条件,可以满足日常开发的所有需求
wrapper就是条件构造器,wrapper就是顶层的,
示例:
- 查询出名字带0,存款大于等于1000的人的id,username,info,balance字段
@Test
void testQueryWrapper() {
// 1.构建查询条件,可以wrapper.实现,也可以下方的链式编程
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
- 更新用户名为jack的用户的余额为2000
@Test
void testQueryWrapper() {
// 1.构建查询条件,可以wrapper.实现,也可以下方的链式编程
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
P6 核心功能-自定义SQL
什么时候自定义SQL:构造where语句时,可以利用mybatis plus构造复杂的where条件,然后自定义SQL语句中剩下的部分。
例如:需要将id在指定范围的用户的余额扣减指定值,可以怎么做?
手写SQL:
<update id = "updateBalanceByIds">
UPDATE user
SET balance = balance - #{amount}
WHERE id IN
<foreach collection="ids" separator="," item="id" open="(" close=")">
#{id}
</foreach>
</update>
以上用mp实现,如下,但是将sql写在了业务层中,不提倡
List<Long> ids = List.of(1L, 2L, 4L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
userMapper.update(null, wrapper);
这里就用到自定义SQL,即使用mybatisplus构建where条件,但是更新查询的sql语句还是自己在mapper中写,然后将条件传递到mapper中去即可。例如下面的步骤:
- 基于wrapper构建where条件
@Test
void testMyCustomSqlUpdate() {
// 自定义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变量名,必须是ew
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("amount") int amount);
- 自定义sql
<update id="updateBalanceByIds">
UPDATE user SET balance = balance - #{amount} ${ew.customSqlSegment}
</update>
P7 核心功能-Iservice接口基本用法
接口基本上还是增删改查,但是很多基本用法,例如批量新增, 批量删除等操作
业务层我们知道,要定义接口并实现,如果接口继承了mybatisplus的IService,但是没有实现里面的方法也是不行的,所以mybatis plus也提供了实现类ServiceImpl,
例如接口的继承:public interface IUserService extends IService<User>
,IService中指定泛型
例如实现类的继承,需要加上两个ServiceImpl<M extends BaseMapper, T extends Object>
P8 核心功能-IService开发基础业务接口
例如:
这里介绍一个知识点,在controller中注入service,之前是使用@autowired,这里不用这种注解,spring中推荐使用构造函数,例如下面:
private final IUserService userService;
public UserController(IUserService userService) {
this.userService = userService;
}
但是上述方法中,如果有多个成员变量名,则还要继续增加,不是很方便
因此可以使用lombok注解,不是全参构造,而是@RequireArgsConstructor,同时给service加上final,表名是一个常量,则在类初始化过程中就必须会完成变量的初始化。
在保存新增的时候要先考虑mybatis plus有没有实现方法。
实现新增接口中可能会遇到属性拷贝的问题,这里介绍一下:通过源码可知,BeanUtils.copyProperties通过java反射将类中当前属性字段对应的内容复制到另外一个类中,属性必须具有get/set方法,不然拷贝值为null。
当然这里遇到新增时拷贝的user中info为空的原因不是因为没有get、set方法,而是因为User实体类中的info属性的注解使用了类型转换器,JacksonTypeHandler.class
被指定为类型转换器,这意味着该字段的数据将通过Jackson库进行序列化和反序列化。Jackson库通常用于处理JSON数据,因此,这个注解可能用于将数据库中的JSON格式数据与Java对象进行映射和转换。例如在数据库中存储的信息为:{"age": 24, "intro": "英文老师", "gender": "female"}
,所以新增的时候也要传入json格式数据。
例如:
import org.springframework.beans.BeanUtils;
BeanUtils.copyProperties(userFormDTO, user);
P9 核心功能-IService开发复杂业务接口
例如扣减用户余额,不能直接写sql对用户进行扣减,要充分考虑各种状态,例如用户状态,用户余额等。
业务实现示例:
@Override
@Transactional
public void deductBalance(Long id, Integer money){
// 获取当前用户
User user = this.getById(id);
// 判断当前状态
if (user == null || user.getStatus() == UserStatus.FROZEN){
throw new RuntimeException("用户状态异常");
}
// 检查余额
if (user.getBalance() < money){
throw new RuntimeException("用户余额不足");
}
// 实现mapper方式
userMapper.deductBalance(id, money);
}
P10 核心功能-IService的Lambda方法
lambda的查询
例如:实现一个根据复杂条件查询用户的接口,查询条件为:
-
name,可以为空
-
status,可以为空
-
minBalance,最小余额可以为空
-
maxBalance,最大余额,可以为空
实现:
controller实现一个请求实体对象接受请求,并在业务层处理,最终返回用户信息,也用VO实体封装,
例如controller的实现
@ApiOperation("根据复杂条件查询用户")
@GetMapping("/list")
public List<UserVO> queryUsers(UserQuery userQuery){
// 将请求传过来的实体中的信息获取传到业务层
List<User> users = userService.queryUsers(
userQuery.getName(), userQuery.getStatus(), userQuery.getMinBalance(), userQuery.getMaxBalance()
);
return BeanUtil.copyToList(users, UserVO.class);
}
使用lamdaquery可直接构建条件。
@Override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
return lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
}
lambda的更新
例如更新余额操作,
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0, User::getStatus, UserStatus.FROZEN)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance()) // 乐观锁,避免多线性造成
.update(); // 上述只是构建条件,这一句是执行更新
P11 核心功能-IService的批量新增
例如:批量插入10万条数据,
-
尝试使用for循环依次插入,时间勉勉强强
-
使用批量插入,考虑到一次网络请求数据量的大小,可以分10x1000进行插入,进行批量新增,效率提升接近10倍,
saveBatch
,会先预编译成sql语句,但是也是一条条的执行 -
把上述预编译sql变换成一条条的数据插入,例如下面的方式,虽然数据很对,但是只有一条sql语句,这样才是真正的批处理
- 使用mybatis plus的批处理,开启rewriteBatchedStatements=true参数(指重写批处理语句,是mysql里面的配置,默认false,改写为true则会转写为下面的形式,实际上是mysql驱动实现的,不是mybatis plus的原因)
实现配置的操作如下所示:末尾加上这个参数并修改为true
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true