Mybatis-Plus mybatis-plus学习笔记,参考B站视频
参考博客
Mybatis-Plus官方文档
快速入门 使用mybatis-plus的基础步骤
引入mybatisPlus的Maven依赖,代替原始Mybatis依赖
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.3.1</version > </dependency >
定义Mapper接口继承自BaseMapper
1 2 3 4 public interface UserMapper extends BaseMapper <User> {}
单元测试即可直接使用
mapper
存在一系列增删改查的方法可以直接调用
小插曲
测试如下方法
1 2 3 4 5 @Test void testQueryByIds () { List<User> users = userMapper.selectBatchIds(List.of(1L , 2L , 3L , 4L )); users.forEach(System.out::println); }
一直报错:
1 java: 找不到符号 符号: 方法 of(long,long,long,long) 位置: 接口 java.util.List
原因:
List.of()
是JDK9
之后才有的版本
解决方法:提升java
编译的JDK
版本
修改如下位置(注意:无论修改ProjectStructure
还是pom.xml
中的java
版本均无效)
常见注解 MyBatisPlus 通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
基本约定
常用注解
@TableName :用来指定表名
@TableId :用来指定表中的主键字段信息
@TableField :用来指定表中的普通字段信息
注意的点:
当实体类和数据库不满足既定的约定的话,就必须要使用对应的注解
对于@TableId
是用来指定主键字段,对于数据库中主键存在一些属性(比如自增),所以注解中有时也要注明主键类型
IdType枚举:
AUTO:数据库自增长
INPUT:通过set方法自行输入
ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法
默认采用雪花算法
使用@TableField的常见场景:
成员变量名与数据库字段名不一致
成员变量名以is开头,且是布尔值
成员变量名与数据库关键字冲突
成员变量不是数据库字段
常见配置 MyBatisPlus 的配置项继承了MyBatis原生配置和一些自己特有的配置。
1 2 3 4 5 6 7 8 9 10 mybatis-plus: type-aliases-package: com.itheima.mp.domain.po # 别名扫描包 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为雪花算法生成 update-strategy: not_null # 更新策略:只更新非空字段
具体可参考官方文档:使用配置 | MyBatis-Plus (baomidou.com)
总结 Mybatis-Plus
使用的基本流程
引入起步依赖
自定义Mapper,继承自BaseMapper
在实体类上添加注解申明信息
在application.yml
中根据需要添加配置
核心功能 条件构造器 Mybatis-Plus
支持各种复杂的where
条件,可以满足日常开发的各种需求
下图中方法参数中的wrapper
其实就是条件构造器
案例 基于QueryWrapper的查询
①查询出名字中带o的,存款大于等于1000元的人的id、username、info、balance字段
1 2 3 4 5 6 7 8 9 10 11 @Test public void testQueryWrapper () { QueryWrapper<User> wrapper = new QueryWrapper <User>() .select("id" ,"username" ,"info" ,"balance" ) .like("username" ,"o" ) .ge("balance" ,1000 ); List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); }
②更新用户名为jack的用户的余额为2000
1 2 3 4 5 6 7 8 9 10 11 @Test public void testUpdateByQueryWrapper () { User user = new User (); user.setBalance(2000 ); QueryWrapper<User> wrapper = new QueryWrapper <User>() .eq("username" ,"jack" ); userMapper.update(user,wrapper); }
基于UpdateWrapper的更新
需求:更新id为1,2,4的用户的余额,扣200
1 2 3 4 5 6 7 8 9 10 @Test public void testUpdateWrapper () { 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); }
基于LambdaQueryWrapper 的查询
1 2 3 4 5 6 7 8 9 10 11 @Test public void testLambdaQueryWrapper () { LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper <User>() .select(User::getId,User::getUsername,User::getInfo,User::getBalance) .like(User::getUsername,"o" ) .ge(User::getBalance,10 ); List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); }
总结
QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
自定义SQL 我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。
需求:将id在指定范围的用户(例如1、2、4 )的余额扣减指定值
背景
直接写SQL语句,where
条件语句比较繁琐
全部用Wrapper
来完成,不符合业务逻辑规范(参数往往只允许在Service
层去定义)
自定义SQL流程 ①基于Wrapper构建where条件
1 2 3 4 5 List<Long> ids = List.of(1L ,2L ,4L ); int amount=-20000 ;LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper <User>() .in(User::getId,ids); userMapper.updateBalanceById(lambdaQueryWrapper,amount);
②在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
1 2 3 4 5 public interface UserMapper extends BaseMapper <User> { void updateBalanceById (@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") int amount) ; }
③自定义SQL,并使用Wrapper条件
1 2 3 <update id ="updateBalanceById" > Update user SET balance = balance - #{amount} ${ew.customSqlSegment} </update >
Service接口 Mybatis-Plus
还提供了Service
层的接口,有一系列可用的增删改查方法
Mybatis-Plus Service接口使用流程
自定义Service接口继承IService接口
1 2 public interface IUserService extends IService <User> {}
自定义Service实现类,实现自定义接口并继承ServiceImpl类
1 2 3 @Service public class UserServiceImpl extends ServiceImpl <UserMapper, User> implements IUserService {}
实例一:基于Restful风格实现下面的接口
引入对应依赖 1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > com.github.xiaoymin</groupId > <artifactId > knife4j-openapi2-spring-boot-starter</artifactId > <version > 4.1.0</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency >
swagger配置信息 本实例借助Swagger
实现接口功能的在线测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 knife4j: enable: true openapi: title: 用户管理接口文档 description: "用户管理接口文档" email: bang@bang.cn concat: bang url: https://www.bang.cn version: v1.0.0 group: default: group-name: default api-rule: package api-rule-resources: - com.itheima.mp.controller
创建对应的实体类
userFormDTO
:代表新增用户的表单
UserVO
:代表查询的返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.itheima.mp.domain.dto;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;@Data @ApiModel(description = "用户表单实体") public class UserFormDTO { @ApiModelProperty("id") private Long id; @ApiModelProperty("用户名") private String username; @ApiModelProperty("密码") private String password; @ApiModelProperty("注册手机号") private String phone; @ApiModelProperty("详细信息,JSON风格") private String info; @ApiModelProperty("账户余额") private Integer balance; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.itheima.mp.domain.vo;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;@Data @ApiModel(description = "用户VO实体") public class UserVO { @ApiModelProperty("用户id") private Long id; @ApiModelProperty("用户名") private String username; @ApiModelProperty("详细信息") private String info; @ApiModelProperty("使用状态(1正常 2冻结)") private Integer status; @ApiModelProperty("账户余额") private Integer balance; }
创建响应结果实体类 1 2 3 4 5 6 7 8 @Data @NoArgsConstructor @AllArgsConstructor public class JsonResult <T>{ private T data; private Integer status; private String message; }
按照restful
风格编写Controller
接口方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 @Api(tags = "用户管理接口") @RequiredArgsConstructor @RestController @RequestMapping("/users") public class UserController { private final IUserService userService; @ApiOperation("新增用户接口") @PostMapping public JsonResult<UserVO> saveUser (@RequestBody UserFormDTO userFormDTO) { User user = BeanUtil.copyProperties(userFormDTO, User.class); userService.save(user); JsonResult result = new JsonResult <UserVO>(null ,200 ,"新增用户成功" ); return result; } @ApiOperation("删除用户接口") @DeleteMapping("/{id}") public JsonResult<UserVO> deleteUserById (@ApiParam("用户id") @PathVariable("id") Long id) { userService.removeById(id); JsonResult<UserVO> result = new JsonResult <UserVO>(null ,200 ,"删除用户成功" ); return result; } @ApiOperation("根据id查询用户接口") @GetMapping("/{id}") public JsonResult<UserVO> queryUserById (@ApiParam("用户id") @PathVariable("id") Long id) { User user = userService.getById(id); UserVO userVO = BeanUtil.copyProperties(user,UserVO.class); JsonResult<UserVO> result = new JsonResult <UserVO>(userVO,200 ,"查询成功" ); return result; } @ApiOperation("根据id批量查询接口") @GetMapping public JsonResult<List<UserVO>> queryUserByIds (@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) { List<User> userList = userService.listByIds(ids); List<UserVO> userVOList = BeanUtil.copyToList(userList, UserVO.class); JsonResult<List<UserVO>> result = new JsonResult <>(userVOList,200 ,"查询成功" ); return result; } @ApiOperation("根据id扣减余额接口") @PutMapping("/{id}/deduction/{money}") public JsonResult<UserVO> reductionBalanceById (@ApiParam("用户id") @PathVariable("id") Long id,@ApiParam("扣减金额") @PathVariable("money") Integer money) { boolean r = userService.reductionBalanceById(id,money); JsonResult<UserVO> result; if (r) result = new JsonResult <UserVO>(null ,200 ,"扣减余额成功" ); else result = new JsonResult <UserVO>(null ,500 ,"扣减余额失败" ); return result; } }
对于需求5,删减指定用户金额,业务逻辑无法借助`Mybatis-plus现提供的方法,所以需要自定义方法
UserMapper
接口
1 void updateBalanceById (@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") int amount) ;
UserMapper.xml
1 2 3 <update id ="updateBalanceById" > Update user SET balance = balance - #{amount} ${ew.customSqlSegment} </update >
IUserService
接口
1 2 3 public interface IUserService extends IService <User> { public boolean reductionBalanceById (Long id,Integer money) ; }
IUserService
接口实现方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service @RequiredArgsConstructor public class UserServiceImpl extends ServiceImpl <UserMapper, User> implements IUserService { private final UserMapper userMapper; @Override public boolean reductionBalanceById (Long id, Integer money) { User user = getById(id); if (user==null || user.getBalance()<money){ return false ; } LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper <User>().eq(User::getId,id); userMapper.updateBalanceById(lambdaQueryWrapper,money); return true ; } }
访问localhost:8080/doc.html
即可测试
实例二:IService的Lambda查询 需求:实现一个根据复杂条件查询用户的接口,查询条件如下:
name:用户名关键字,可以为空
status:用户状态,可以为空
minBalance:最小余额,可以为空
maxBalance:最大余额,可以为空
上述功能其实就是多条件筛选,如果采用传统自己编写SQL
语句的话,会非常繁琐
如果采用IService
提供的lambdaQuery
的话会非常简洁
在IUserServiceImpl
中实现如下方法
1 2 3 4 5 6 7 8 @Override public List<User> queryUsersByCondition (String name, Integer status, Integer minBalance, Integer maxBalance) { return lambdaQuery().like(name!=null ,User::getUsername,name) .eq(status!=null ,User::getStatus,status) .gt(minBalance!=null ,User::getBalance,minBalance) .lt(maxBalance!=null ,User::getBalance,maxBalance) .list(); }
UserController
添加的代码
1 2 3 4 5 6 7 8 9 10 11 12 @ApiOperation("根据条件查询用户接口") @GetMapping("/list") public JsonResult<List<UserVO>> queryUsers (UserQuery userQuery) { List<User> userList = userService.queryUsersByCondition(userQuery.getName(), userQuery.getStatus() , userQuery.getMinBalance(), userQuery.getMaxBalance()); List<UserVO> userVOS = BeanUtil.copyToList(userList, UserVO.class); JsonResult<List<UserVO>> result = new JsonResult <>(userVOS, 200 , "查询成功" ); return result; }
案例三:IService的Lambda更新 需求:改造根据id修改用户余额的接口,要求如下
①完成对用户状态校验
②完成对用户余额校验
③如果扣减后余额为0,则将用户status修改为冻结状态(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public boolean reductionBalanceById (Long id, Integer money) { User user = getById(id); if (user==null || user.getBalance()<money){ return false ; } int remainBalance = user.getBalance()-money; lambdaUpdate().set(User::getBalance,remainBalance) .set(remainBalance==0 ,User::getStatus,2 ) .eq(User::getId,id) .eq(User::getBalance,user.getBalance()) .update(); return true ; }
案例四:Iservice批量新增 需求:批量插入10万条用户数据,并作出对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void testSaveByFor () { long start = System.currentTimeMillis(); for (int i=0 ;i<100000 ;i++){ userService.save(buildUser(i)); } long end = System.currentTimeMillis(); System.out.printf("耗费时间:" +(end-start)+" ms" ); } @Test public void testSaveByBatch () { List<User> list = new ArrayList <>(1000 ); long start = System.currentTimeMillis(); for (int i=0 ;i<100000 ;i++){ list.add(buildUser(i)); if (i%1000 ==0 ){ userService.saveBatch(list); list.clear(); } } long end = System.currentTimeMillis(); System.out.printf("耗费时间:" +(end-start)+" ms" ); }
循环插入所需时间
批次插入所需时间
开启rewriteBatchedStatements
参数
1 url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
结论
批处理方案:
扩展功能 代码生成 Mybatis-plus
使用流程
定义数据库对应的实体类
定义mapper
接口并继承自BaseMapper
定义xxService
接口并继承自IService
定义xxService
实现类,并继承自ServiceImpl
Mybatis-Plus
提供了这些代码的自动生成,官方文档
利用MybatisPlus
插件来生成对应代码
使用步骤
静态工具 静态工具中的方法与IService
中的方法比较相似,其由于是静态的,不用创建出对象即可调用;但是需要传入数据库对应实体类的class
(泛型)
案例:静态工具查询 需求:
①改造根据id查询用户的接口,查询用户的同时,查询出用户对应的所有地址
②改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址
③实现根据用户id查询收货地址功能,需要验证用户状态,冻结用户抛出异常(练习)
上次需求的实现如果按照常规实现方式时,就会出现循环依赖 ,UserService
中会注入AddressService
,AddressService
中也会注入UserService
此时如借助静态工具,则无需相互注入,解决循环依赖问题
创建地址数据表VO类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Data @ApiModel(description = "收货地址VO") public class AddressVO { @ApiModelProperty("id") private Long id; @ApiModelProperty("用户ID") private Long userId; @ApiModelProperty("省") private String province; @ApiModelProperty("市") private String city; @ApiModelProperty("县/区") private String town; @ApiModelProperty("手机") private String mobile; @ApiModelProperty("详细地址") private String street; @ApiModelProperty("联系人") private String contact; @ApiModelProperty("是否是默认 1默认 0否") private Boolean isDefault; @ApiModelProperty("备注") private String notes; }
改写UserVO,新增地址属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Data @ApiModel(description = "用户VO实体") public class UserVO { @ApiModelProperty("用户id") private Long id; @ApiModelProperty("用户名") private String username; @ApiModelProperty("详细信息") private String info; @ApiModelProperty("使用状态(1正常 2冻结)") private Integer status; @ApiModelProperty("账户余额") private Integer balance; @ApiModelProperty("收货地址列表") private List<AddressVO> addresss; }
根据Id查询单个用户信息及其收货地址 改写UserController 中的queryUserById
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @ApiOperation("根据id查询用户接口") @GetMapping("/{id}") public JsonResult<UserVO> queryUserById (@ApiParam("用户id") @PathVariable("id") Long id) { UserVO userVO = userService.queryUserAndAddressById(id); if (userVO==null ){ return new JsonResult <>(null ,600 ,"查询失败" ); }else { return new JsonResult <>(userVO,200 ,"查询成功" ); } }
新增IUserService 中的queryUserAndAddressById
方法
1 UserVO queryUserAndAddressById (Long id) ;
新增UserServiceImpl 中的queryUserAndAddressById
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Override public UserVO queryUserAndAddressById (Long id) { User user = getById(id); if (user==null || user.getStatus()==2 ){ return null ; } UserVO userVO = BeanUtil.copyProperties(user, UserVO.class); List<Address> addressList = Db.lambdaQuery(Address.class) .eq(Address::getUserId, id) .list(); if (CollUtil.isNotEmpty(addressList)){ List<AddressVO> addressVOS = BeanUtil.copyToList(addressList, AddressVO.class); userVO.setAddresses(addressVOS); } return userVO; }
根据id列表查询多个用户信息及其收货地址 改写UserController 中的queryUserByIds
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @ApiOperation("根据id批量查询接口") @GetMapping public JsonResult<List<UserVO>> queryUserByIds (@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) { List<UserVO> userVOS = userService.listUserAndAddressByIds(ids); if (userVOS==null ) return new JsonResult <>(null ,600 ,"查询失败" ); else return new JsonResult <>(userVOS,200 ,"查询成功" ); }
新增IUserService 中的queryUserAndAddressByIds
方法
1 List<UserVO> listUserAndAddressByIds (List<Long> ids) ;
新增UserServiceImpl 中的queryUserAndAddressByIds
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Override public List<UserVO> listUserAndAddressByIds (List<Long> ids) { List<User> userList = listByIds(ids); if (CollUtil.isEmpty(userList)){ return null ; } List<Address> addressList = Db.lambdaQuery(Address.class) .in(Address::getUserId, ids) .list(); List<UserVO> userVOS = BeanUtil.copyToList(userList, UserVO.class); if (CollUtil.isNotEmpty(addressList)){ List<AddressVO> addressVOS = BeanUtil.copyToList(addressList, AddressVO.class); Map<Long, List<AddressVO>> map = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId)); for (UserVO userVO : userVOS) { userVO.setAddresses(map.get(userVO.getId())); } } return userVOS; }
逻辑删除 逻辑删除 就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:
在表中添加一个字段标记数据是否被删除
当删除数据时把标记置为1
查询时只查询标记为0的数据
背景:淘宝中的购物订单模块,用户点击订单删除按钮,本地会消失,但是实际上数据库中该数据并未被删除,应为对于商家而言,订单数据比较重要;此时采用的就是逻辑删除 逻辑,用户查询时不会查询该数据,但该数据在数据库中仍然存在
MybatisPlus 提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:
1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名,字段类型可以是boolean、integer logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
注意
逻辑删除本身也有自己的问题,比如:
因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。
枚举处理器 Java
中的枚举类型与数据库整数
类型之间的转换问题
实现PO
类中的枚举变量与数据库字段的转换 ①给枚举中的与数据库对应value值添加@EnumValue注解
②在配置文件中配置统一的枚举处理器,实现类型转换
1 2 3 mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
实例 定义Status字段对应的枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Getter public enum UserStatus { Normal(1 ,"正常" ), FREEZE(2 ,"冻结" ); @EnumValue private int value; private String description; UserStatus(int value, String description) { this .value = value; this .description = description; } }
配置文件中定义Mybatis枚举处理器
1 2 3 mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
将UserPO和UserVO类中的Status属性改成枚举类型
枚举类型属性返回值的更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Getter public enum UserStatus { Normal(1 ,"正常" ), FREEZE(2 ,"冻结" ); @EnumValue private int value; @JsonValue private String description; UserStatus(int value, String description) { this .value = value; this .description = description; } }
JSON处理器 Java
数据类型与数据库中Json
数据类型的转换
Json处理器使用流程
在VO/PO实体类的对应属性上添加typeHandler
属性,让对应处理器生效
在实体类的@TableName
注解中开启autoResultMap
,对象嵌套过程中的自动映射
实例 创建数据库Json字段对应的实体类
1 2 3 4 5 6 7 8 @Data @NoArgsConstructor @AllArgsConstructor(staticName = "of") public class UserInfo { private Integer age; private String intro; private String gender; }
修改实体类User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @Data @TableName(value = "user",autoResultMap = true) public class User { @TableId("id") private Long id; @TableField("`username`") private String username; private String password; private String phone; @TableField(typeHandler = JacksonTypeHandler.class) private UserInfo info; private UserStatus status; private Integer balance; private LocalDateTime createTime; private LocalDateTime updateTime; }
更改VO实体类
插件功能 MyBatisPlus基于MyBatis的Interceptor实现了一个基础拦截器,并在内部保存了MyBatisPlus的内置拦截器的集合
MyBatisPlus提供的内置拦截器有下面这些
其中最常用的是分页插件
分页插件 分页插件的配置 首先,要在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class MybatisConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor (); pageInterceptor.setMaxLimit(1000L ); interceptor.addInnerInterceptor(pageInterceptor); return interceptor; } }
分页API的使用
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void testPage () { int pageNo=1 ; int pageSize=5 ; Page<User> page = Page.of(pageNo, pageSize); page.addOrder(new OrderItem ("balance" ,true )); Page<User> p = userService.page(page); System.out.println("total=" +p.getTotal()); System.out.println("pages=" +p.getPages()); List<User> records = page.getRecords(); records.forEach(System.out::println); }
通用分页实体 实例:简单分页查询 需求:遵循下面的接口规范,编写一个UserController接口,实现User的分页查询
返回值类
创建通用的查询参数实体
1 2 3 4 5 6 7 8 9 10 11 12 @Data @ApiModel("查询通用实体") public class PageQuery { @ApiModelProperty("页码") private Integer pageNo; @ApiModelProperty("页大小") private Integer pageSize; @ApiModelProperty("排序字段") private String sortBy; @ApiModelProperty("是否升序") private boolean isAsc; }
用户查询继承与查询实体类
1 2 3 4 5 6 7 8 9 10 11 12 @Data @ApiModel(description = "用户查询条件实体") public class UserQuery extends PageQuery { @ApiModelProperty("用户名关键字") private String name; @ApiModelProperty("用户状态:1-正常,2-冻结") private Integer status; @ApiModelProperty("余额最小值") private Integer minBalance; @ApiModelProperty("余额最大值") private Integer maxBalance; }
创建通用的查询结果实体
1 2 3 4 5 6 7 8 9 10 @Data @ApiModel("通用查询结果实体类") public class QueryDTO <T> { @ApiModelProperty("数据总数") private Long total; @ApiModelProperty("页面总数") private Long pages; @ApiModelProperty("当前页面数据") private List<T> data; }
Service层编写对应方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public QueryDTO<UserVO> queryUsersByPage (UserQuery query) { String name = query.getName(); Integer status = query.getStatus(); Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); if (StrUtil.isNotEmpty(query.getSortBy())){ page.addOrder(new OrderItem (query.getSortBy(),query.isAsc())); }else { page.addOrder(new OrderItem ("update_time" ,false )); } Page<User> pages = lambdaQuery().like(name != null , User::getUsername, name) .eq(status != null , User::getStatus, status) .page(page); QueryDTO<UserVO> userVOPageDTO = new QueryDTO <>(); List<UserVO> userVOS = BeanUtil.copyToList(pages.getRecords(), UserVO.class); userVOPageDTO.setTotal(pages.getTotal()); userVOPageDTO.setPages(pages.getPages()); userVOPageDTO.setData(userVOS); return userVOPageDTO; }
编写对应的Controller层方法
1 2 3 4 5 6 7 8 @ApiOperation("根据条件分页查询") @GetMapping("/page") public JsonResult<QueryDTO<UserVO>> queryUsersByPage (UserQuery userQuery) { QueryDTO<UserVO> userVOPageDTO = userService.queryUsersByPage(userQuery); return new JsonResult <>(userVOPageDTO,200 ,"查询成功" ); }
优化:函数封装 service
层中page
分页条件的构建和根据查询结果构造分页查询结果实体的代码与业务无关,可以单独抽取出来进行封装
在PageQuery中定义方法,将PageQuery对象转为MyBatisPlus中的Page对象
在PageDTO中定义方法,将MyBatisPlus中的Page结果转为PageDTO结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Data @ApiModel("查询通用实体") public class PageQuery { @ApiModelProperty("页码") private Integer pageNo; @ApiModelProperty("页大小") private Integer pageSize; @ApiModelProperty("排序字段") private String sortBy; @ApiModelProperty("是否升序") private boolean isAsc; public <PO> Page<PO> toMybatisPage () { Page<PO> page = Page.of(pageNo, pageSize); if (StrUtil.isNotEmpty(sortBy)){ page.addOrder(new OrderItem (sortBy,isAsc)); }else { page.addOrder(new OrderItem ("update_time" ,false )); } return page; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Data @ApiModel("通用查询结果实体类") public class QueryDTO <T> { @ApiModelProperty("数据总数") private Long total; @ApiModelProperty("页面总数") private Long pages; @ApiModelProperty("当前页面数据") private List<T> data; public static <PO,VO> QueryDTO<VO> toPageDTO (Page<PO> pages,Class<VO> clazz) { QueryDTO<VO> userVOPageDTO = new QueryDTO <>(); List<VO> userVOS = BeanUtil.copyToList(pages.getRecords(),clazz); userVOPageDTO.setTotal(pages.getTotal()); userVOPageDTO.setPages(pages.getPages()); userVOPageDTO.setData(userVOS); return userVOPageDTO; } }
service层方法改写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public QueryDTO<UserVO> queryUsersByPage (UserQuery query) { String name = query.getName(); Integer status = query.getStatus(); Page<User> page = query.toMybatisPage(); Page<User> pages = lambdaQuery().like(name != null , User::getUsername, name) .eq(status != null , User::getStatus, status) .page(page); return QueryDTO.toPageDTO(pages, UserVO.class); }