MyBatis Plus 【详解】| 学习日志 | 第 17 天

发布于:2025-09-03 ⋅ 阅读:(21) ⋅ 点赞:(0)

 目录

        

        MyBatis Plus 

        常用注解

        常见配置

        条件构造器

        1.QueryWrapper

        2.UpdateWrapper

        3.LambdaQueryWrapper

        自定义SQL

        IService接口

        代码生成

        1.安装插件

        2.连接数据库

        3.生成代码

        Db静态工具

        逻辑删除

        枚举处理器

        JSON处理器

        插件功能

        1.分页插件

        2.通用分页实体

        2.1通用分页实体初步实现

        2.2通用分页实体进一步实现

        2.3通用分页实体和MP转换


        MyBatis Plus 

        MyBatis Plus基本操作:

        1.引入依赖。

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

        2.mapper继承extends BaseMapper<E>。

        MyBatis Plus值做增强不做改变,引入它不会对现有工程造成影响,只需要简单配置可以进行单表CRUD操作。

        MyBatis Plus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。约定大于配置。

        默认:

        MybatisPlus会把PO实体的类名驼峰转下划线作为表名

        MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型

        MybatisPlus会把名为id的字段作为主键


        常用注解

        @TableName

        描述:表名注解,标识实体类对应的表

        使用位置:实体类

        @TableId

        描述:主键注解,标识实体类中的主键字段

        使用位置:实体类的主键字段

        支持两个属性,value,type默认IdType.NONE常用IdType.AUTO

        @TableField

        描述:普通字段注解

        成员变量名于数据库阻断不一致

        成员变量名is开头的Boolean类型

        成员变量名与数据库关键字冲突

        成员变量不是数据库的字段@TableField(exist = false)进行标记


        常见配置

        application.yml中大多数配置默认就行需要来就自己配。

mybatis-plus:
  type-aliases-package: com.ithema.mp.domain.po
  global-config:
    db-config:
      id-type: auto

        条件构造器

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

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

        1.QueryWrapper

        AbstractWrapper提供了where中包含的所有条件构造方法。

        QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段。

@Test
    void testQueryWrapper() {

        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id", "username", "info", "balance") // 使用数据库实际列名
                .like("username", "o")
                .ge("balance", 800);

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

        2.UpdateWrapper

        UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分。    

@Test
    void testUpdateWrapper() {
        List<Long> list = new ArrayList<>();
        Collections.addAll(list, 1L, 2L);
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")
                .in("id", list);
        userMapper.update(null, userUpdateWrapper);
    }

        3.LambdaQueryWrapper

        无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

        LambdaQueryWrapper,LambdaUpdateWrapper分别对应QueryWrapper和UpdateWrapper。

        方法一:使用lambda方法。

@Test
    void testQueryWrapper() {

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                .like(User::getUsername, "o")
                .ge(User::getBalance, 1000);

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

        这其中运用了方法引用中的类名引用实例方法。

        如select中的参数是SFunction<T, ?>... columns,也就是需要多个SFunction<T, ?>,而它的代码中标注了@FunctionalInterface是一个函数式接口。

@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}

        它唯一的抽象方法继承自Function,其中R apply(T t); 没有方法体{},只有分号结尾,而无修饰符​​将默认隐式修饰public abstract,所以是该函数式接口的唯一抽象方法。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

        这一方法的第一个参数需要泛型对象,User类对象满足该抽象方法的第一个参数,返回泛型R类型,getId等成员方法依旧满足返回类型。

        方法二: 使用LambdaQueryWrapper,LambdaUpdateWrapper。

@Test
    void testLambdaQueryWrapper() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>()
                .select(User::getId, User::getBalance, User::getUsername, User::getInfo)
                .like(User::getUsername, "o")
                .ge(User::getBalance, 1000);
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        System.out.println(users);
    }

        这其中所用到的反射机制,如图。


        自定义SQL

        MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL。

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

        1.基于Wrapper构建where条件

@Test
    void testQueryWrapper() {
        List<Long> list = new ArrayList<>();
        Collections.addAll(list, 1L, 2L);
        int amount = 200;
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .in(User::getId, list);
        userMapper.updateBalanceByIds(wrapper, amount);
    }

        2.在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaUpdateWrapper<User> wrapper, @Param("amount") int amount);

        3.自定义SQL,并使用wrapper条件

<update id="updateBalanceByIds">
        UPDATE user SET balance = balance - #{amount} ${ew.customSqlSegment}
    </update>

        IService接口

        继承的关系如图。接口IUserService继承IService。

        

public interface IUserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

        实现增加用户。

@SpringBootTest
class IUserServiceTest {
    @Autowired
    private IUserService iUserService;
    @Test
    void testSaveUser() {
        User user = new User();
        user.setId(6L);
        user.setUsername("Lii");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        iUserService.save(user);
    }

}

        基于Restful风格,开发基础业务接口。

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

    private final IUserService userService;
    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        //1.把DTO拷贝到PO
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        //2.新增
        userService.save(user);
    }

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

        userService.removeById(id);
    }

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

    @ApiOperation("根据id批量查询用户接口")
    @GetMapping
    public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
        //1.查询用户PO
        List<User> users = userService.listByIds(ids);
        //2.把PO保存到VO
        return BeanUtil.copyToList(users, UserVO.class);
    }

}

        开发复杂业务接口。

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

    private final IUserService userService;
   
    @ApiOperation("根据name,status和blance批量查询用户接口")
    @GetMapping("/list")
    public List<UserVO> queryUsers(UserQuery query){
        //1.查询用户PO
        List<User> users = userService.queryUsers(query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());
        //2.把PO保存到VO
        return BeanUtil.copyToList(users, UserVO.class);
    }

}
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {
    @ApiModelProperty("用户名关键字")
    private String name;
    @ApiModelProperty("用户状态:1-正常,2-冻结")
    private Integer status;
    @ApiModelProperty("余额最小值")
    private Integer minBalance;
    @ApiModelProperty("余额最大值")
    private Integer maxBalance;
}
public interface UserMapper extends BaseMapper<User> {

}
public interface IUserService extends IService<User> {
    List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @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) //等
                .gt(minBalance != null, User::getBalance, minBalance) //greater than
                .lt(maxBalance != null, User::getBalance, maxBalance) //less than
                .list();

    }
}

        IService批量新增

        MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。这个参数的默认值是false,我们需要修改连接参数,将其配置为true。修改项目中的application.yml文件,在jdbc的url后面添加参&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
@SpringBootTest
class IUserServiceTest {
    @Autowired
    private IUserService iUserService;
  
    private User buildUser(int i) {
        User user = new User();
        user.setUsername("user_" + i);
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(2000);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        return user;
    }

    @Test
    void saveBatchUsers() {
        List<User> list = new ArrayList<>(10000);
        for (int i = 0; i < 10000; i++) {
            list.add(buildUser(i));
        }
        iUserService.saveBatch(list, 1000);
    }
}

        代码生成

        1.安装插件

        

        2.连接数据库

        点击Other,点击Config Database

        

        3.生成代码

        点击Other,点击Code Generator,选中要生成代码的表,选择相应的参数即可生成代码。


        Db静态工具

        为了解决循环依赖(当你尝试创建 A 的实例时,它的构造函数会去创建 B。当创建 B 的实例时,它的构造函数又会去创建 A。创建 A 又会去创建 B,创建 B 又会去创建 A...这将导致​​无限递归​​,最终程序会抛出 StackOverflowError(栈溢出错误)),使用Db静态工具可以解决。

// 文件: A.java
public class A {
    // Class A 依赖(需要)Class B 的一个实例
    private B b;
    public A() {
        this.b = new B();
    }
}

// 文件: B.java
public class B {
    // Class B 又依赖(需要)Class A 的一个实例
    private A a;
    public B() {
        this.a = new A();
    }
}

        使用Db静态工具完成按Id查找用户和地址。

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


    @ApiOperation("根据id查询用户和地址接口")
    @GetMapping("{id}")
    public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){

        return userService.queryUserAndAddressById(id);
    }


}
public interface IUserService extends IService<User> {
   
    UserVO queryUserAndAddressById(Long id);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public UserVO queryUserAndAddressById(Long id) {
        //1.查询用户
        User user = getById(id);
        if (user == null || user.getStatus() == 2) {
            throw new RuntimeException("用户状态异常");
        }

        //2.查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id)
                .list();

        //3.封装VO
        //3.1转User为UserVO
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        //3.2转addresses为List<AddressVO>
        if (CollUtil.isNotEmpty(addresses)) {
            userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
        }

        return userVO;
    }

}

        使用Db静态工具完成按Ids批量查找用户和地址。

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

return userService.queryUserAndAddressById(id);

    @ApiOperation("根据ids批量查询用户和地址接口")
    @GetMapping
    public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){

        return userService.queryUserAndAddressByIds(ids);

    }


}
public interface IUserService extends IService<User> {

    List<UserVO> queryUserAndAddressByIds(List<Long> ids);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    
    @Override
    public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {

        //1.查询用户
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)) {
            return Collections.emptyList();
        }

        //2.查询地址
        //2.1获取用户id集合
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        //2.2根据用户id查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        //2.3转换地址VO
        List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
        //***2.4按用户id分组,返回给一个Map 键:id,值:addressVOs
        Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
        if(CollUtil.isNotEmpty(addressVOS)) {
            addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }

        //3.转换VO
        //提前定义好List<UserVO>的大小和查到的users一样
        List<UserVO> list = new ArrayList<>(users.size());
        for (User user : users) {
            //3.1转换用户VO
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            list.add(userVO);
            //3.2转换地址VO
            userVO.setAddresses(addressMap.get(user.getId()));

        }
        return list;
    }

}

        逻辑删除

        可以给表配置一个字段deleted然后再配置文件中添加下面的代码实现逻辑删除。

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

        枚举处理器

        1.在application.yaml文件中添加配置,使用枚举处理器。

mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

        2.定义枚举,给枚举中的与数据库对应value值添加@EnumValue注解

public enum UserStatus {
    NORMAL(1, "正常"),
    FROZEN(2, "冻结"),
    ;
    @EnumValue //告诉mp是把value往数据库中写
    private final int value;
    @JsonValue //告诉SpringMVC展示数据是desc
    private final String desc;

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

        3.修改PO,VO中的成员变量的类型为UserStatus,在赋值比较时使用UserStatus.NORMAL和UserStatus.FROZEN。


        JSON处理器

        1.定义一个类用来定义Json数据。

@Data
//@AllArgsConstructor
@NoArgsConstructor //Jackson 默认使用无参构造器创建对象实例如果只注解@AllArgsConstructor将没有无参构造
//两个注解都不使用,类中没有显式定义任何构造器,所以Java编译器自动生成了一个默认的无参构造器。
@AllArgsConstructor(staticName = "of") //定义一个构造的静态方法
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}

        2.修改PO,VO中的userInfo,PO需要添加@TableName(value = "user", autoResultMap = true),@TableField(typeHandler = JacksonTypeHandler.class)两个注解,修改类型为UserInfo,VO只用修改类为UserInfo。

@Data
@TableName(value = "user", autoResultMap = true)
//autoResultMap自动结果映射​​,启用后可以处理复杂类型映射:
//JSON 类型字段
//枚举类型
//自定义类型处理器
//嵌套对象映射
public class User {

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



    /**
     * 用户名
     */
    private String username;

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

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

    /**
     * 详细信息
     */
    @TableField(typeHandler = JacksonTypeHandler.class)
    //数据库存储时:Java 对象 → JSON 字符串
    //数据库读取时:JSON 字符串 → Java 对象
    private UserInfo info;

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

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

    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    @TableField(exist = false)
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {

    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private UserInfo info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private UserStatus status;

    @ApiModelProperty("账户余额")
    private Integer balance;

    @ApiModelProperty("用户的收货地址")
    private List<AddressVO> addresses;
}

        插件功能

        1.分页插件

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

//声明这是一个配置类,相当于XML配置文件
//Spring容器启动时,会扫描到@Configuration注解的类
@Configuration
public class MybatisConfig {
    @Bean //相当于XML中的<bean>标签
    //两者都是告诉 Spring 容器:
    //"请创建这个对象"
    //"请管理这个对象的生命周期"
    //"请将这个对象提供给其他需要它的组件"这里创建的是interceptor
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //1.初始化核心插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //2.添加分页插件
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        pageInterceptor.setMaxLimit(1000L); // 设置分页上限
        interceptor.addInnerInterceptor(pageInterceptor);
        return interceptor;
    }
}

        接着使用分页的API,IService中的IPage。

        

        常使用IPage的子类Page。

        开始查询。

@Test
    void testPageQuery() {
        int PageNum = 1;
        int PageSize = 2;
        //1.准备分页条件
        //1.1分页条件
        Page<User> page = Page.of(PageNum, PageSize);
        //1.2排序条件
        page.addOrder(new OrderItem("balance", true));
        page.addOrder(new OrderItem("id", true));
        //2.执行分页查询
        Page<User> resPage = iUserService.page(page);
        //3.解析
        System.out.println(resPage.getTotal());
        System.out.println(resPage.getPages());
        List<User> users = resPage.getRecords();
        users.forEach(System.out::println);
    }

        2.通用分页实体

        2.1通用分页实体初步实现

        首先创建一个通用的分页查询query实体。

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {

    @ApiModelProperty("页码")
    private Integer pageNum;
    @ApiModelProperty("页大小")
    private Integer pageSize;
    @ApiModelProperty("排序")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;

}

        修改UserQuery继承查询分页查询实体。


@EqualsAndHashCode(callSuper = true)
@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;
}

        创建分页查询结果存储的PageDTO。

@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Long total;

    @ApiModelProperty("总页数")
    private Long pages;

    @ApiModelProperty("集合")
    private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致
}

        创建Controller中分页查询接口。

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


    @ApiOperation("根据条件分页查询用户接口")
    @GetMapping("/page")
    public PageDTO<UserVO> queryUsersPage(UserQuery query){

        return userService.queryUsersPage(query);
    }

}

        创建ServiceImpl中执行分页查询的方法。

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    
    @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        //1.构建查询条件
        //1.1分页条件
        Page<User> page = Page.of(query.getPageNum(), query.getPageSize());
        //1.2排序条件
        if(StrUtil.isNotBlank(query.getSortBy())){
            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
        } else {
            //为空则按时间排序
            page.addOrder(new OrderItem("update_time", false));
        }

        //2.分页查询
        Page<User> resPage = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);
        //3.封装VO结果
        //3.1存储查询返回参数到DTO
        PageDTO<UserVO> pageDTO= new PageDTO<>();
        pageDTO.setTotal(resPage.getTotal());
        pageDTO.setPages(resPage.getPages());
        
        //3.2存储查询数据到PO
        List<User> users = resPage.getRecords();
        //没查到数据则返回空DTO
        if(CollUtil.isEmpty(users)) {
            pageDTO.setList(Collections.emptyList());
            return pageDTO;
        }
        //3.3存储查询数据到VO中
        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
        
        //3.4存储查询数据到DTO
        pageDTO.setList(userVOS);
        //4.返回
        return pageDTO;
    }

}

        2.2通用分页实体进一步实现

        改造PageQuery,创建一个静态泛型方法让其来做创建查询条件。

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {

    @ApiModelProperty("页码")
    private Integer pageNum = 1;
    @ApiModelProperty("页大小")
    private Integer pageSize = 5;
    @ApiModelProperty("排序")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc = true;

    public <T> Page<T> toMpPage(OrderItem...items) {
        //第一个 <T>:类型参数声明,声明这是一个泛型方法
        //第二个 T:返回类型中的具体化,指定返回类型为Page<T>
        //1.构建查询条件
        //1.1分页条件
        Page<T> page = Page.of(pageNum, pageSize);
        //1.2排序条件
        if(StrUtil.isNotBlank(sortBy)){
            //不为空
            page.addOrder(new OrderItem(sortBy, isAsc));
        } else if(items != null){
            //为空则按默认排序
            page.addOrder(items);
        }
        return page;
    }

    public <T> Page<T> toMpPageDefaultSortByCreateTime() {
        return toMpPage(new OrderItem("create_time", false));
    }
    public <T> Page<T> toMpPageDefaultSortByUpdateTime() {
        return toMpPage(new OrderItem("update_time", true));
    }
    public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc) {
        return toMpPage(new OrderItem(defaultSortBy, defaultAsc));
    }

}

        改造PageDTO<T>创建静态泛型方法让其来做PO到VO再存储到DTO的转换。

@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Long total;

    @ApiModelProperty("总页数")
    private Long pages;

    @ApiModelProperty("集合")
    private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致

    public static <PO, VO> PageDTO<VO> of(Page<PO> resPage, Class<VO> clazz) {
        //3.封装PO到VO结果到DTO
        //3.1存储查询返回参数到DTO
        PageDTO<VO> pageDTO= new PageDTO<>();
        //总条数
        pageDTO.setTotal(resPage.getTotal());
        //总页数
        pageDTO.setPages(resPage.getPages());

        //3.2存储查询页数据到PO
        List<PO> po = resPage.getRecords();
        //没查到数据则返回空DTO
        if(CollUtil.isEmpty(po)) {
            pageDTO.setList(Collections.emptyList());
            return pageDTO;
        }
        //3.3拷贝查询页数据到VO中
        //List<VO> userVOS = BeanUtil.copyToList(users, VO.class);
        //调用者才知道VO的字节码所以public static <PO, VO> PageDTO<VO> of(Page<PO> page, Class<VO> clazz)
        //传递参数 Class<VO> clazz
        List<VO> vos = BeanUtil.copyToList(po, clazz);

        //3.4存储查询数据到DTO
        pageDTO.setList(vos);
        //4.返回
        return pageDTO;

    }
}

        修改Service简化分页查询。

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

   

    @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
//        //1.构建查询条件
//        //1.1分页条件
//        Page<User> page = Page.of(query.getPageNum(), query.getPageSize());
//        //1.2排序条件
//        if(StrUtil.isNotBlank(query.getSortBy())){
//            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
//        } else {
//            //为空则按时间排序
//            page.addOrder(new OrderItem("update_time", false));
//        }
        //1.构建查询条件
        Page<User> page = query.toMpPageDefaultSortByUpdateTime();

        //2.分页查询
        Page<User> resPage = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);
//        //3.封装VO结果
//        //3.1存储查询返回参数到DTO
//        PageDTO<UserVO> pageDTO= new PageDTO<>();
//        pageDTO.setTotal(resPage.getTotal());
//        pageDTO.setPages(resPage.getPages());
//
//        //3.2存储查询数据到PO
//        List<User> users = resPage.getRecords();
//        //没查到数据则返回空DTO
//        if(CollUtil.isEmpty(users)) {
//            pageDTO.setList(Collections.emptyList());
//            return pageDTO;
//        }
//        //3.3存储查询数据到VO中
//        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
//
//        //3.4存储查询数据到DTO
//        pageDTO.setList(userVOS);
//        //4.返回
//        return pageDTO;
        return PageDTO.of(resPage, UserVO.class);
    }

}

        2.3通用分页实体和MP转换

        修改PageDTO静态泛型方法中的参数为函数式皆苦,让Service自定义查询数据的转换。

@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Long total;

    @ApiModelProperty("总页数")
    private Long pages;

    @ApiModelProperty("集合")
    private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致

   
    public static <PO, VO> PageDTO<VO> of1(Page<PO> resPage, Function<PO, VO> convertor) {
        //3.封装PO到VO结果到DTO
        //3.1存储查询返回参数到DTO
        PageDTO<VO> pageDTO= new PageDTO<>();
        //总条数
        pageDTO.setTotal(resPage.getTotal());
        //总页数
        pageDTO.setPages(resPage.getPages());

        //3.2存储查询页数据到PO
        List<PO> po = resPage.getRecords();
        //没查到数据则返回空DTO
        if(CollUtil.isEmpty(po)) {
            pageDTO.setList(Collections.emptyList());
            return pageDTO;
        }
        //3.3拷贝查询页数据到VO中
        List<VO> vos = po.stream().map(convertor).collect(Collectors.toList());

        //3.4存储查询数据到DTO
        pageDTO.setList(vos);
        //4.返回
        return pageDTO;

    }

        修改Service并使用lambda表达式定义自己的转换逻辑。

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deductBalance(Long id, Integer money) {
        //查询用户
        User user = getById(id);
        //校验用户状态
        if(user == null|| user.getStatus() == UserStatus.FROZEN) {
            throw new RuntimeException("用户状态异常");
        }
        //校验余额是否充足
        if(user.getBalance() < money) {
            throw new RuntimeException("用户余额不足");
        }
        //扣减余额
        baseMapper.deductBalance(id, money);

    }

    @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) //等
                .gt(minBalance != null, User::getBalance, minBalance) //greater than
                .lt(maxBalance != null, User::getBalance, maxBalance) //less than
                .list();

    }

    @Override
    public UserVO queryUserAndAddressById(Long id) {
        //1.查询用户
        User user = getById(id);
        if (user == null || user.getStatus() == UserStatus.FROZEN) {
            throw new RuntimeException("用户状态异常");
        }

        //2.查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id)
                .list();

        //3.封装VO
        //3.1转User为UserVO
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        //3.2转addresses为List<AddressVO>
        if (CollUtil.isNotEmpty(addresses)) {
            userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
        }

        return userVO;
    }

    @Override
    public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {

        //1.查询用户
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)) {
            return Collections.emptyList();
        }

        //2.查询地址
        //2.1获取用户id集合
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        //2.2根据用户id查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        //2.3转换地址VO
        List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
        //***2.4按用户id分组,返回给一个Map 键:id,值:addressVOs
        Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
        if(CollUtil.isNotEmpty(addressVOS)) {
            addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }

        //3.转换VO
        //提前定义好List<UserVO>的大小和查到的users一样
        List<UserVO> list = new ArrayList<>(users.size());
        for (User user : users) {
            //3.1转换用户VO
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            list.add(userVO);
            //3.2转换地址VO
            userVO.setAddresses(addressMap.get(user.getId()));

        }
        return list;
    }

    @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
//        //1.构建查询条件
//        //1.1分页条件
//        Page<User> page = Page.of(query.getPageNum(), query.getPageSize());
//        //1.2排序条件
//        if(StrUtil.isNotBlank(query.getSortBy())){
//            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
//        } else {
//            //为空则按时间排序
//            page.addOrder(new OrderItem("update_time", false));
//        }
        //1.构建查询条件
        Page<User> page = query.toMpPageDefaultSortByUpdateTime();

        //2.分页查询
        Page<User> resPage = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);
//        //3.封装VO结果
//        //3.1存储查询返回参数到DTO
//        PageDTO<UserVO> pageDTO= new PageDTO<>();
//        pageDTO.setTotal(resPage.getTotal());
//        pageDTO.setPages(resPage.getPages());
//
//        //3.2存储查询数据到PO
//        List<User> users = resPage.getRecords();
//        //没查到数据则返回空DTO
//        if(CollUtil.isEmpty(users)) {
//            pageDTO.setList(Collections.emptyList());
//            return pageDTO;
//        }
//        //3.3存储查询数据到VO中
//        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
//
//        //3.4存储查询数据到DTO
//        pageDTO.setList(userVOS);
//        //4.返回
//        return pageDTO;
        return PageDTO.of1(resPage, user -> {
            //1.拷贝基础属性(PO转VO)
            UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
            //2.处理特殊逻辑(其他的操作)
            vo.setUsername(vo.getUsername().substring(0, vo.getUsername().length() - 2) + "**");
            return vo;
        });
    }

}


网站公告

今日签到

点亮在社区的每一天
去签到