MybatisPlus

1 快速入门

1.1 入门案例

  • 需求:基于课前资料提供的项目,实现下列功能:

    新增用户功能
    根据 id 查询用户
    根据 id 批量查询用户
    根据 id 更新用户
    根据 id 删除用户
  • 步骤 

1.引入MybatisPlus的起步依赖 

MyBatisPlus官方提供了starter,其中集成了MybatisMybatisPlus的所有功能,并且实现了自动装配效果。

因此我们可以用MybatisPlusstarter代替Mybatisstarter

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

2.定义Mapper

自定义的Mapper继承MybatisPlus提供的BaseMapper接口:

public interface UserMapper extends BaseMapper<User> {

//    void saveUser(User user);
//
//    void deleteUser(Long id);
//
//    void updateUser(User user);
//
//    User queryUserById(@Param("id") Long id);
//
//    List<User> queryUserByIds(@Param("ids") List<Long> ids);
}

 

1.2 常见注解

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

•类名驼峰转下划线作为表名
•名为id的字段作为主键
•变量名驼峰转下划线作为表的字段名

MybatisPlus中比较常用的几个注解如下:

@TableName :用来指定表名
@TableId :用来指定表中的主键字段信息
@TableField :用来指定表中的普通字段信息

IdType枚举:

•AUTO:数据库自增长
•INPUT:通过set方法自行输入
•ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法

使用@TableField的常见场景:

•成员变量名与数据库字段名不一致
•成员变量名以is开头,且是布尔值
•成员变量名与数据库关键字冲突
•成员变量不是数据库字段

例如有这样的表结构:

@TableName("tb_user")
public class User {
    @TableId(value="id",type=IdType.AUTO)
    private Long id;
    @TableField("username")
    private String name;
    @TableField("is_married")
    private Boolean isMarried;
    @TableField("`order`")
    private Integer order;
    @TableField(exist = false)
    private String address;
}

总结

MybatisPlus是如何获取实现CRUD的数据库表信息的?

默认以类名驼峰转下划线作为表名
默认把名为 id 的字段作为主键 
默认把变量名驼峰转下划线作为表的字段名

MybatisPlus的常用注解有哪些?

@TableName :指定表名称及全局配置
@TableId :指定 id 字段及相关配置
@TableField :指定普通字段及相关配置

IdType的常见类型有哪些?

AUTO ASSIGN_ID INPUT

使用@TableField的常见场景是?

成员变量名与数据库字段名不一致
成员变量名以 is 开头,且是布尔值
成员变量名与数据库关键字冲突
成员变量不是数据库字段

1.3 常用配置

MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:

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 # 更新策略:只更新非空字段

1.4 总结

MyBatisPlus使用的基本流程是什么?

引入起步依赖
自定义 Mapper 基础 BaseMapper
在实体类上添加注解声明 表信息
application.yml 中根据需要添加配置

2 核心功能

2.1 条件构造器

  •    MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。

Lambda是基于lambda语法

  • 案例:基于QueryWrapper的查询

需求:

查询出名字中带 o 的,存款大于等于 1000 元的人的 id username info balance 字段
    
SELECT id,username,info,balance 
FROM user 
WHERE username LIKE ? AND balance >= ?
@Test
    void testQueryWrapper(){

        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id","username","info","balance")
                        .like("username","o")
                                .ge("balance","1000");


        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);

    }

更新用户名为 jack 的用户的余额为 2000
UPDATE user 
    SET balance = 2000 
    WHERE (username = "jack")
@Test
    void testUpdateByQueryWrapper(){
        User user = new User();
        user.setBalance(2000);

        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .eq("username","jack");

        userMapper.update(user,wrapper);
    }
  • 案例:基于UpdateWrapper的更新

需求:更新id1,2,4的用户的余额,扣200

UPDATE user 
    SET balance = balance - 200 
    WHERE id in (1, 2, 4)
@Test
    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);
    }
  • LambdaUpdateWrapper
@Test
    void testLambdaQueryWrapper(){

        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);
        users.forEach(System.out::println);

    }

 条件构造器的用法

  • QueryWrapperLambdaQueryWrapper通常用来构建selectdeleteupdatewhere条件部分
  • UpdateWrapperLambdaUpdateWrapper通常只有在set语句比较特殊才使用
  • 尽量使用LambdaQueryWrapperLambdaUpdateWrapper,避免硬编码

2.2 自定义SQL

我们可以利用MyBatisPlusWrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。

例如这个语句如果单纯用mp是很难写出来的

SELECT status, COUNT(id) AS total
FROM tb_user
<where>
    <if test="name != null">AND username LIKE #{name}</if>
    <if test="ids != null">
        AND id IN
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</where>
GROUP BY status

解决步骤

基于 Wrapper 构建 where 条件
List<Long> ids=List.of(1L,2L,4L);
        Integer amount=200;
        LambdaQueryWrapper<User> wrapper=new LambdaQueryWrapper<User>()
                .in(User::getId,ids);
        userMapper.updateBalanceById(wrapper,amount);

mapper 方法参数中用 Param 注解声明 wrapper 变量名称,必须是 ew
    void updateBalanceById(@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") Integer amount);
自定义 SQL ,并使用 Wrapper 条件

 <update id="updateBalanceById">
        update user set balance=balance-#{amount} ${ew.customSqlSegment}
    </update>

2.3 IService

  • MPService接口使用流程是怎样的?
  • 自定义Service接口继承IService接口​​​​​​
public interface UserService extends IService<User> {
}
  • 自定义Service实现类,实现自定义接口并继承ServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}
  •  test
package com.itheima.mp.service;

import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;
import java.util.List;


@SpringBootTest
public class IUSerServiceTest {

    @Autowired
    private UserService userService;


    @Test
    void testSaveUSer(){
        User user = new User();
        //user.setId(5L);
        user.setUsername("LiLei");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());


        userService.save(user);
    }

    @Test
    void testQuery(){
        List<User> users = userService.listByIds(List.of(1L, 2L, 3L));
        users.forEach(System.out::println);
    }
}

 案例:基于Restful风格实现下列接口

需求:基于Restful风格实现下面的接口:

编号

接口

请求方式

请求路径

请求参数

返回值

1

新增用户

POST

/users

用户表单实体

2

删除用户

DELETE

/users/{id}

用户id

3

根据id查询用户

GET

/users/{id}

用户id

用户VO

4

根据id批量查询

GET

/users

用户id集合

用户VO集合

5

根据id扣减余额

PUT

/users/{id}/deduction/{money}

用户 id
扣减金额

  •  加入web和swagger依赖
<!--swagger-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
            <version>4.1.0</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  •  配置swagger依赖
knife4j:
  enable: true
  openapi:
    title: 用户管理接口文档
    description: "用户管理接口文档"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.itheima.mp.controller
    
  • domain.dto.UserFormDTO 
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;
}
  •  domin.vo.Uservo
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;
}
  • controller.UserContrller
package com.itheima.mp.controller;


import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {


    private final UserService userService;

    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUSer(@RequestBody UserFormDTO userFormDTO){
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        userService.save(user);

    }

    @ApiOperation("删除用户接口")
    @DeleteMapping("/{id}")
    public void deleteUserById(@ApiParam("id") @PathVariable Long id){
        userService.removeById(id);
    }

    @ApiOperation("根据id查询用户接口")
    @GetMapping("/{id}")
    public UserVO queryUserById(@ApiParam("id") @PathVariable Long id){
        User user = userService.getById(id);
        return BeanUtil.copyProperties(user,UserVO.class);
    }

    @ApiOperation("根据id批量查询用户接口")
    @GetMapping
    public List<UserVO> queryUserByIds(@ApiParam("ids") @PathVariable List<Long> ids){
        List<User> users = userService.listByIds(ids);
        return BeanUtil.copyToList(users,UserVO.class);
    }


}

自定义service方法

@ApiOperation("扣减用户余额接口")
    @PutMapping("/{id}/deduction/{money}")
    public void deductMoneyById(
            @ApiParam("id") @PathVariable Long  id,
            @ApiParam("money")@PathVariable Integer money
    ){
        userService.deductBalance(id,money);
    }
package com.itheima.mp.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public void deductBalance(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);


    }
}
    @Update("update user set balance=balance-#{money} where id=#{id}")
    void deductBalance(Long id, Integer money);

案例:IServiceLambda查询

需求:实现一个根据复杂条件查询用户的接口,查询条件如下:

name :用户名关键字,可以为空
status :用户状态,可以为空
minBalance :最小余额,可以为空
maxBalance :最大余额,可以为空 
@Override
    public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
        List<User> list = 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();


        return list;
    }

案例:IServiceLambda更新

需求:改造根据id修改用户余额的接口,要求如下

完成对用户状态校验
完成对用户余额校验
如果扣减后余额为 0 ,则将用户 status 修改为冻结状态( 2
@Override
    public void deductBalance(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("用户余额不足");
        }

        Integer remainBalance=user.getBalance()-money;

        boolean update = lambdaUpdate()
                .set(User::getBalance, remainBalance)
                .set(remainBalance == 0, User::getStatus, 2)
                .eq(User::getId, id)
                .eq(User::getBalance, user.getBalance())
                .update();


    }

案例:IService批量新增

需求:批量插入10万条用户数据,并作出对比:

普通 for 循环插入
IService 的批量插入
开启 rewriteBatchedStatements =true 参数
  • 使用普通for循环插入
@Test
    void testSaveOneByOne(){
        Long begin=System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            userService.save(builderUser(i));
        }
        Long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-begin));
    }
  • 使用iservice的批量插入,mp底层会对list集合进行预编译为sql语句,
  • @Test
        void testSaveBatch(){
            List<User> list=new ArrayList<>(1000);
            Long begin=System.currentTimeMillis();
            for (int i = 1; i <= 100000; i++) {
                list.add(builderUser(i));
                if(i%1000==0){
                    userService.saveBatch(list);
                    list.clear();
                }
            }
            Long end=System.currentTimeMillis();
            System.out.println("耗时:"+(end-begin));
    
        }
  • 开启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: Pengjixuan0524.

批处理方案:

•普通for循环逐条插入速度极差,不推荐
•MP的批量新增,基于预编译的批处理,性能不错
•配置jdbc参数,开rewriteBatchedStatements,性能最好

3. 拓展功能

3.1 静态工具

案例:静态工具查询

需求:

改造根据 id 查询用户的接口,查询用户的同时,查询出用户对应的所有地址
 @Override
    public UserVO queryUserAndAddress(Long id) {
        User user = this.lambdaQuery().eq(User::getId, id).one();
        if(user==null||user.getStatus()==2){
            throw  new RuntimeException("用户不存在或被冻结");
        }
        List<Address> list = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        if(list!=null){
            userVO.setAddressVOS(BeanUtil.copyToList(list, AddressVO.class));
        }
        return userVO;
    }

当出现多个service方法相互调用的情况的时候,如果直接使用依赖注入,那么会出现循环依赖的的局面,这是可以使用Db工具类。 

改造根据 id 批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址
@Override
    public List<UserVO> queryUserAndAddressByids(List<Long> ids) {
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)){
            return Collections.emptyList();
        }
        List<Long> listId = users.stream().map(User::getId).collect(Collectors.toList());
        List<Address> address = Db.lambdaQuery(Address.class).in(Address::getUserId, ids).list();
        List<AddressVO> addressVOS = BeanUtil.copyToList(address, AddressVO.class);
        Map<Long, List<AddressVO>> addressMap=new HashMap<>(0);
        if(CollUtil.isEmpty(addressMap)) {
            addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }
        ArrayList<UserVO> userVOS = new ArrayList<>(users.size());
        for (User user : users) {
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            userVO.setAddressVOS(addressMap.get(user.getId()));
            userVOS.add(userVO);

        }
        return userVOS;
    }
实现根据用户 id 查询收货地址功能,需要验证用户状态,冻结用户抛出异常(练习)

3.2 逻辑删除

逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:

在表中添加一个字段标记数据是否被删除
当删除数据时把标记置为 1
查询时只查询标记为 0 的数据

例如逻辑删除字段为deleted

删除操作:
  1. UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0
    
查询操作:
SELECT * FROM user WHERE deleted = 0

MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      logic-delete-field: deleted

注意:

逻辑删除本身也有自己的问题,比如:

会导致数据库表垃圾数据越来越多,影响查询效率
SQL 中全都需要对逻辑删除字段做判断,影响查询效率

因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。

3.3 枚举处理器

将User类 里面的用户状态字段由Integer类型转换为枚举类型,优点就是可读性加强。

但同样带来了一个问题,Java当中的枚举类型和数据库中中的类型相互转换的问题

解决方案:

  • 第一步,为枚举类型中的value字段加一个@EnumValue注解,作用是可以和数据库中的类型相同,知道把谁写进数据库。
package com.itheima.mp.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import io.swagger.models.auth.In;
import lombok.Getter;


@Getter
public enum UserStatus {
    NORMAL(1,"普通"),
    FREEZE(2,"冻结"),
    ;

    @EnumValue
    private final Integer value;
    private final String desc;

    UserStatus(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}
  • 第二步,配置yaml文件
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

注意,最后写进数据库的值是枚举的值,如果想改变值的表现形式,可以在特定的属性是加上@JSONValue。

3.4 JSON处理器

数据库中user表中有一个json类型的字段:

在Java中一般拿String类型与之对应,但当要用到info中的信息的时候,就会产生不便利,要想操作更方便应该定义一个实体类,来更json格式相对应。

操作步骤:

1. 定义一个实体类

2.在User类中的info字段加一个@TableField(typeHandler = JacksonTypeHandler.class),在类上加一个@TableName(value = "User",autoResultMap = true)。

package com.itheima.mp.domain.po;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}
package com.itheima.mp.domain.po;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName(value = "User",autoResultMap = true)
public class User {

    /**
     * 用户id
     */
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    /**
     * 用户名
     */

    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 注册手机号
     */
    private String phone;

    /**
     * 详细信息
     */
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;

    /**
     * 使用状态(1正常 2冻结)
     */
    private UserStatus status;

    /**
     * 账户余额
     */
    private Integer balance;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

4.插件功能

MyBatisPlus提供的内置拦截器有下面这些:

序号

拦截器

描述

1

TenantLineInnerInterceptor

多租户插件

2

DynamicTableNameInnerInterceptor

动态表名插件

3

PaginationInnerInterceptor

分页插件

4

OptimisticLockerInnerInterceptor

乐观锁插件

5

IllegalSQLInnerInterceptor

SQL性能规范插件,检测并拦截垃圾SQL

6

BlockAttackInnerInterceptor

防止全表更新和删除的插件

4.1 分页插件

首先,要在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:

package com.itheima.mp.config;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor=new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setMaxLimit(1000L);
        mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
        return mybatisPlusInterceptor;
    }

}

接着,就可以使用分页的API了:

@Test
    public void testPageQuery(){
        int pageNo=1,pageSize=2;
        Page<User> page=Page.of(pageNo,pageSize);
        page.addOrder(new OrderItem("balance",true));
        Page<User> page1 = userService.page(page);
        long total = page1.getTotal();
        System.out.println("total: "+total);
        long pages = page1.getPages();
        System.out.println("pages: "+pages);
        List<User> records = page1.getRecords();
        records.forEach(System.out::println);
    }

4.2 通用分页实体

需求:遵循下面的接口规范,编写一个UserController接口,实现User的分页查询

  • UserQuery
package com.itheima.mp.domain.query;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@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;
}
  • PageQuery
package com.itheima.mp.domain.query;


import lombok.Data;

@Data
public class PageQuery {

    private Long pageNo;
    private Long pageSize;
    private String sortBy;
    private Boolean isAsc;
}
  • PageDto
package com.itheima.mp.domain.dto;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDto {
    private Long Total;
    private Long pages;
    private List list;
}
  • UserController
@ApiOperation("根据id批量查询用户接口")
    @GetMapping("/page")
    public PageDto queryUsersPage(UserQuery query){
        return userService.queryUrersPage(query);


    }
  • UserServiceImpl
@Override
    public PageDto queryUrersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
        if(query.getSortBy()!=null){
            page.addOrder(new OrderItem(query.getSortBy(),true));
        }else{
            page.addOrder(new OrderItem("update_time",false));
        }
        Page<User> page1 = lambdaQuery().like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);

        PageDto pageDto=new PageDto();
        pageDto.setTotal(page1.getTotal());
        pageDto.setPages(page1.getPages());
        List<User> records = page1.getRecords();
        if(CollUtil.isEmpty(records)){
            pageDto.setList(Collections.emptyList());
        }else{
            List<UserVO> userVOS = BeanUtil.copyToList(records, UserVO.class);
            pageDto.setList(userVOS);
        }

        return pageDto;


    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值