Mybatis-Plus
快速开始
-
引入 MybatisPlus 的起步依赖
- MyBatisPlus官方提供了 starter ,其中 集成了 Mybatis 和 MybatisPlus 的所有功能,并且 实现了自动装配效果。因此我们可以 用MybatisPlus的starter 代替 Mybatis的starter
1 2 3 4 5
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency>
|
-
定义 Mapper
- 自定义的 Mapper 继承 MybatisPlus 提供的 BaseMapper 接口
1 2 3 4
| public interface UserMapper extends BaseMapper<User> { }
|

常用注解
MyBatisPlus 通过扫描实体类,并基于反射获取实体类信息作为数据库表信息

- 泛型中的 User 就是与数据库对应的 PO
- MybatisPlus 就是根据 PO实体 的信息来推断出 表的信息,从而生成SQL的。默认情况下:
- MybatisPlus 会把 PO实体 的 类名驼峰转下划线 作为 表名
- MybatisPlus 会把 PO实体 的 所有变量名驼峰转下划线 作为 表的字段名,并根据变量类型推断 字段类型
- MybatisPlus 会把名为 id 的字段作为 主键
@TableName
- 描述:表名注解,标识实体类对应的表
- 使用位置:实体类
eg:
1 2 3 4 5
| @TableName("user") public class User { private Long id; private String name; }
|
@TableId
- 描述:主键注解,标识实体类中的主键字段
- 使用位置:实体类的主键字段
eg:
1 2 3 4 5 6 7
| @TableName("user") public class User { @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; }
|
@TableId 支持两个属性:

IdType 支持的类型:这里只给出比较常见的
AUTO:利用数据库的 id 自增长
INPUT:手动生成 id
ASSIGN_ID:雪花算法 生成Long类型的 全局唯一id,这是默认的ID策略
@TableField
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @TableName("user") public class User { @TableId private Long id; @TableField("username") private String name; private Integer age; @TableField("isMarried") private Boolean isMarried; @TableField("`concat`") private String concat; @TableField(exist = false) private String address; }
|
一般情况下并不需要给字段添加@TableField注解,一些特殊情况除外:
- 成员变量名 与 数据库字段名 不一致
- 成员变量是以
isXXX 命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
- 成员变量名与数据库一致,但是 与数据库的关键字冲突。使用
@TableField注解给字段名添加转义字符:``
- 成员变量 不是数据库字段
常见配置
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" configuration: map-underscore-to-camel-case: true cache-enabled: false global-config: db-config: id-type: assign_id update-strategy: not_null
|
总结:
MyBatisPlus 使用的 基本流程 是:
- 引入起步依赖
- 自定义 Mapper 基础 BaseMapper
- 在实体类上添加注解声明 表信息
- 在 application.yml 中根据需要添加配置
条件构造器
除了 新增 以外,修改、删除、查询 的SQL语句都需要指定 where 条件。因此 BaseMapper 中提供的相关方法 除了以 id 作为 where 条件以外,还支持更加复杂的 where 条件。

参数中的 Wrapper 就是条件构造的抽象类,其下有很多默认实现,继承关系如图:




QueryWrapper
无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Test void testQueryWrapper() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.select("id", "username", "info", "balance") .like("username", "o") .ge("balance", 1000); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Test void testUpdateByQueryWrapper() { User user = new User(); user.setBalance(2000); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username","jack"); userMapper.update(user, wrapper); }
|
UpdateWrapper
基于 BaseMapper 中的 update 方法更新时只能直接赋值,对于一些复杂的需求就难以实现
eg:
1 2 3 4 5 6 7 8 9 10 11
|
@Test void testUpdateWrapper() { List<Long> ids = Arrays.asList(1L, 2L, 4L); UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.setSql("balance = balance - 200") .in("id", ids); userMapper.update(null, wrapper); }
|
LambdaQueryWrapper
无论是 QueryWrapper 还是 UpdateWrappe r在构造条件的时候都需要 写死字段名称,会出现字符串魔法值
其中一种办法是 基于变量的gettter方法 结合 反射技术 。因此只要将条件对应的字段的 getter方法 传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的 方法引用 和 Lambda 表达式
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test void testLambdaQueryWrapper() { LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.select(User::getId, User::getUsername, User::getInfo, User::getBalance) .like(User::getUsername, "o") .ge(User::getBalance, 1000); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
|
总结:
条件构造器的用法:
- QueryWrapper 和 LambdaQueryWrapper 通常用来构建 select 、delete 、update 的 where 条件部分
- UpdateWrapper 和 LambdaUpdateWrapper 通常只有在 set 语句比较特殊才使用
- 尽量使用 LambdaQueryWrapper 和 LambdaUpdateWrapper,避免硬编码
自定义SQL
利用 MyBatisPlus 的 Wrapper 来 构建复杂的 Where 条件,然后自己定义 SQL 语句中剩下的部分

步骤:
-
基于 Wrapper 构建 where 条件
1 2 3 4 5 6 7 8 9
|
List<Long> ids = Arrays.asList(1L, 2L, 4L); int amount = 200;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId, ids);
userMapper.updateBalanceByIds(wrapper, amount);
|
-
在 mapper 方法参数中用 Param 注解声明 wrapper 变量名称,必须是 ew
1 2 3
| void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount") int amount);
|
-
自定义SQL,并使用 Wrapper 条件
1 2 3 4 5
| <update id="updateBalanceByIds"> UPDATE tb_user SET balance = balance - #{amount} ${ew.customSqlSegment} </update>
|
Service 接口
MybatisPlus不仅提供了 BaseMapper ,还提供了通用的 Service 接口及默认实现,封装了一些常用的 service模板方法
通用接口为 IService ,默认实现为 ServiceImpl ,其中封装的方法可以分为以下几类:
save:新增
remove:删除
update:更新
get:查询单个结果
list:查询集合结果
count:计数
page:分页查询
新增

save 是 新增单个元素
saveBatch 是 批量新增
saveOrUpdate 是 根据id 判断,如果数据存在就更新,不存在则新增
saveOrUpdateBatch 是 批量的新增或修改
删除

removeById:根据 id 删除
removeByIds:根据 id 批量删除
removeByMap:根据 Map 中的 键值对 为条件删除
remove(Wrapper<T>):根据 Wrapper 条件删除
~~removeBatchByIds~~:暂不支持
修改

updateById:根据 id 修改
update(Wrapper<T>):根据 UpdateWrapper 修改,Wrapper 中包含 set 和 where 部分
update(T,Wrapper<T>):按照 T 内的数据修改与 Wrapper 匹配到的数据
updateBatchById:根据 id 批量修改
查询

getById:根据 id 查询1条数据
getOne(Wrapper<T>):根据 Wrapper 查询1条数据
getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper

listByIds:根据 id 批量查询
list(Wrapper<T>):根据 Wrapper 条件查询 多条数据
list():查询 所有

count():统计 所有数量
count(Wrapper<T>):统计符合 Wrapper 条件的 数据数量

基本用法
由于Service中经常 需要定义与业务有关的自定义方法,因此我们不能直接使用 IService ,而是自定义 Service 接口,然后继承 IService 以拓展方法。同时,让自定义的 Service实现类 继承 ServiceImpl ,这样就不用自己实现 IService 中的接口了
-
自定义 Service 接口 继承 IService 接口
1 2
| public interface IUserService extends IService<User> { }
|
-
自定义 Service 实现类,实现自定义接口并继承 ServiceImpl 类
1 2 3
| public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { }
|
eg:
需求:基于 Restful 风格实现下面的接口

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 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
@RestController @RequestMapping("/users") @Api(tags = "用户管理接口") @RequiredArgsConstructor public class UserController {
private final IUserService userService;
@PostMapping @ApiOperation("新增用户接口") public void saveUser(@RequestBody UserFormDTO userDTO) { User user = BeanUtil.copyProperties(userDTO, User.class);
userService.save(user); }
@DeleteMapping("{id}") @ApiOperation("删除用户接口") public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id) { userService.removeById(id); }
@GetMapping("{id}") @ApiOperation("根据id查询用户接口") public UserVO QueryUserById(@ApiParam("用户id") @PathVariable("id") Long id) { User user = userService.getById(id);
return BeanUtil.copyProperties(user, UserVO.class); }
@GetMapping @ApiOperation("根据id批量查询用户接口") public List<UserVO> QueryUserById(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids) { List<User> users = userService.listByIds(ids); return BeanUtil.copyToList(users, UserVO.class); }
@PutMapping("/{id}/deduction/{money}") @ApiOperation("根据id扣减余额接口") public void deductBalanceById(@ApiParam("用户id") @RequestParam("id") Long id , @ApiParam("扣减的金额") @RequestParam("money") Integer money) { userService.deductBalanceById(id, money); } }
|
1 2 3 4 5 6 7
|
public interface UserMapper extends BaseMapper<User> {
@Update("update mp.user set balance = balance - #{money} where id = #{id}") void deductBalance(@Param("id") Long id, @Param("money") Integer money); }
|
1 2 3 4 5
|
public interface IUserService extends IService<User> { void deductBalanceById(Long id, Integer money); }
|
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
|
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override public void deductBalanceById(Long id, Integer money) { User user = this.getById(id);
if (user == null || user.getStatus() == 2) { throw new RuntimeException("用户状态异常"); }
if (user.getBalance() < money) { throw new RuntimeException("用户余额不足"); }
baseMapper.deductBalance(id, money);
} }
|
Lambda

LambdaQuery的查询方法使用
案例:实现一个根据复杂条件查询用户的接口,查询条件如下:
- name:用户名关键字,可以为空
- status:用户状态,可以为空
- minBalance:最小余额,可以为空
- maxBalance:最大余额,可以为空
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
@GetMapping("/list") @ApiOperation("根据复杂条件查询用户接口") public List<UserVO> queryUsers(UserQuery query) { List<User> queryUsers = userService.queryUsers (query.getName(), query.getStatus(), query.getMaxBalance(), query.getMinBalance()); return BeanUtil.copyToList(queryUsers, UserVO.class); }
|
1 2 3
|
List<User> queryUsers(String name, Integer status, Integer maxBalance, Integer minBalance);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@Override public List<User> queryUsers(String name, Integer status, Integer maxBalance, Integer minBalance) { List<User> users = lambdaQuery() .like(name != null, User::getUsername, name) .eq(status != null, User::getStatus, status) .le(maxBalance != null, User::getBalance, maxBalance) .ge(minBalance != null, User::getBalance, minBalance) .list(); return users; }
|
LambdaUpdate的更新方法使用
需求:改造根据id修改用户余额的接口,要求如下:
- 完成对用户状态校验
- 完成对用户余额校验
- 如果扣减后余额为0,则将用户status修改为冻结状态
eg:
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
| @Override @Transactional public void deductBalanceById(Long id, Integer money) { User user = this.getById(id); if (user == null || user.getStatus() == 2) { throw new RuntimeException("用户状态异常"); } if (user.getBalance() < money) { throw new RuntimeException("用户余额不足"); } 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(); }
|
IService 批量新增
-
批处理方案:普通 for 循环 逐条插入 速度极差,不推荐
-
MP 的批量新增,基于 预编译 的 批处理 ,性能不错
-
配置 JDBC 参数(在 yml 文件里),开 rewriteBatchedStatements,性能最好
1
| rewriteBatchedStatements=true
|
代码生成




静态工具
有的时候 Service 之间也会相互调用,为了 避免出现循环依赖 问题,MybatisPlus 提供一个 静态工具类 :Db,其中的一些 静态方法 与 IService中方法签名 基本一致,也可以帮助我们实现 CRUD 功能。

需求:
- 改造根据 id 查询用户的接口,查询用户的同时,查询出用户对应的所有地址
- 改造根据 id 批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@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> addresses; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Override public UserVO queryUserAndAddressById(Long id) { User user = lambdaQuery().eq(id != null, User::getId, id).one(); if (user == null || user.getStatus() == 2) { throw new RuntimeException("用户状态异常"); } List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list(); UserVO userVO = BeanUtil.copyProperties(user, UserVO.class); if (CollUtil.isNotEmpty(addresses)) { List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class); userVO.setAddresses(addressVOS); } return 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 31 32 33 34 35 36 37
| @Override public List<UserVO> queryUserAndAddressByIds(List<Long> ids) { List<User> users = this.listByIds(ids); if (CollUtil.isEmpty(users)) { throw new RuntimeException("用户为空"); } List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList()); List<Address> addresses = Db.lambdaQuery(Address.class) .in(Address::getUserId, userIds) .list(); List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class); Map<Long, List<AddressVO>> map = null; if (CollUtil.isNotEmpty(addressVOList)) { map = addressVOList .stream() .collect(Collectors.groupingBy(AddressVO::getUserId)); } List<UserVO> list = new ArrayList<>(users.size()); for (User user : users) { UserVO userVO = BeanUtil.copyProperties(user, UserVO.class); list.add(userVO); userVO.setAddresses(map.get(user.getId())); } return list; }
|
逻辑删除
逻辑删除就是 基于代码逻辑 模拟删除 效果,但并 不会 真正 删除数据。
思路如下:
- 在表中 添加一个字段 标记数据 是否 被删除
- 当 删除数据 时把标记置为 1
- 查询时 只查询标记为 0 的数据

MybatisPlus 提供了 逻辑删除 功能,无需改变方法调用的方式,而是在底层自动修改 CRUD 的语句。只需要在 application.yaml 文件中 配置逻辑删除 的 字段名称 和 值 即可。
1 2 3 4 5 6 7
| mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0
|
逻辑删除本身也有 自己的问题,比如:
- 会 导致数据库表垃圾数据 越来越多,影响查询效率
- SQL中全都需要对 逻辑删除字段 做判断,影响查询效率
因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以 采用把数据迁移到其它表 的办法
枚举处理器
MybatisPlus提供了一个 处理枚举 的 类型转换器 ,可以帮我们 把枚举类型与数据库类型自动转换
eg:
定义一个用户状态的枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @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; } }
|
把 User 类中的status字段改为UserStatus 类型
1 2 3 4 5 6 7
|
private Integer status;
private UserStatus status;
|
在 application.yml 中配置 全局枚举处理器 :
1 2 3
| mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
JSON处理器
MybatisPlus 提供了很多 特殊类型字段 的 类型处理器,解决 特殊字段类型 与 数据库类型 转换的问题。例如处理 JSON 就可以使用 JacksonTypeHandler 处理器

eg:
1 2 3 4 5 6 7
| @Data public class UserInfo { private Integer age; private String intro; private String gender; }
|
1 2 3 4 5 6 7 8 9 10 11
| @Data @TableName(value = "user", autoResultMap = true) public class User { private Long id; private String username; @TableField(typeHandler = JacksonTypeHandler.class) private UserInfo info; }
|
插件功能
MyBatisPlus 提供的 内置拦截器 如下:

分页插件
步骤:
-
要在 配置类config文件夹 中注册 MyBatisPlus 的核心插件,同时添加 分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Configuration public class MybatisConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); pageInterceptor.setMaxLimit(1000L); interceptor.addInnerInterceptor(pageInterceptor); return interceptor; } }
|
-
使用分页的API


这个 Page类 下面一般存储页的数量、页的大小等等
-
编写一个 分页查询 的测试
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
| @Test void testPageQuery() { int pageNo = 1, pageSize = 5; Page<User> page = Page.of(pageNo, pageSize); page.addOrder(new OrderItem("balance", false)); page.addOrder(new OrderItem("id", true)); Page<User> p = userService.page(page); System.out.println("total = " + p.getTotal()); System.out.println("pages = " + p.getPages()); List<User> records = p.getRecords(); records.forEach(System.out::println); }
|
通用分页实体
主要是封装统一的查询条件(UserQuery)以及统一的返回值(PageDTO)
需求:遵循下面的接口规范,编写一个 UserController 接口,实现 User 的 分页查询

eg:
这里需要定义3个实体:
UserQuery:分页查询条件的实体,包含分页、排序参数、过滤条件
PageDTO:分页结果实体,包含总条数、总页数、当前页数据
UserVO:用户页面视图实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Data @EqualsAndHashCode(callSuper = true) @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 11 12 13 14 15 16 17 18
|
@Data @ApiModel(description = "分页查询实体") 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 13 14 15 16 17 18 19 20 21 22 23 24
|
@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> addresses; }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
@Data @ApiModel(description = "分页结果") public class PageDTO<T> { @ApiModelProperty("总条数") private Long total; @ApiModelProperty("总页数") private Long pages; @ApiModelProperty("集合") private List<T> list; }
|
控制层代码以及实现接口代码:略
在 UserServiceImpl 中实现该方法
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
| public PageDTO<UserVO> queryUsersPage(UserQuery query) { String name = query.getName(); Integer maxBalance = query.getMaxBalance(); Integer minBalance = query.getMinBalance(); Integer status = query.getStatus(); Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); if (StrUtil.isNotBlank(query.getSortBy())) { page.addOrder(new OrderItem(query.getSortBy(), false)); } else { page.addOrder(new OrderItem("update_time", false)); } Page<User> p = lambdaQuery() .like(name != null, User::getUsername, name) .eq(status != null, User::getStatus, status) .le(maxBalance != null, User::getBalance, maxBalance) .ge(minBalance != null, User::getBalance, minBalance) .page(page); PageDTO<UserVO> dto = new PageDTO<>(); dto.setPages(p.getPages()); dto.setTotal(p.getTotal()); List<User> records = p.getRecords(); if (CollUtil.isEmpty(records)) { dto.setList(Collections.emptyList()); return dto; } List<UserVO> userVOList = BeanUtil.copyToList(records, UserVO.class); dto.setList(userVOList); return dto; }
|
改造PageQuery实体
在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很麻烦。
可以将其封装到 PageDTO 的工具方法中
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
| @Data @ApiModel(description = "分页查询实体") public class PageQuery {
@ApiModelProperty("页码") private Integer pageNo = 1;
@ApiModelProperty("页的大小") private Integer pageSize = 5;
@ApiModelProperty("排序字段") private String sortBy;
@ApiModelProperty("是否升序") private boolean isAsc = true;
public <T> Page<T> toMpPage(OrderItem... items) { Page<T> page = Page.of(pageNo, pageSize);
if (StrUtil.isNotBlank(sortBy)) { page.addOrder(new OrderItem(sortBy, isAsc)); } else { page.addOrder(items); }
return page; }
public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){ return this.toMpPage(new OrderItem(defaultSortBy, isAsc)); }
public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() { return toMpPage(new OrderItem("create_time", false)); }
public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() { return toMpPage(new OrderItem("update_time", false)); } }
|
改造PageDTO实体
在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很麻烦。
可以将其封装到 PageDTO 的工具方法中
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
| @Data @NoArgsConstructor @AllArgsConstructor public class PageDTO<V> { private Long total; private Long pages; private List<V> list;
public static <V, P> PageDTO<V> empty(Page<P> p){ return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList()); }
public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) { List<P> records = p.getRecords(); if (records == null || records.size() <= 0) { return empty(p); } List<V> vos = BeanUtil.copyToList(records, voClass); return new PageDTO<>(p.getTotal(), p.getPages(), vos); }
public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) { List<P> records = p.getRecords(); if (records == null || records.size() <= 0) { return empty(p); } List<V> vos = records.stream().map(convertor).collect(Collectors.toList()); return new PageDTO<>(p.getTotal(), p.getPages(), vos); } }
|