Mybatis plue(二) 扩展功能、插件功能

发布于:2024-04-05 ⋅ 阅读:(89) ⋅ 点赞:(0)

扩展功能

P12 扩展功能-代码生成器

  • 方法一:mybatisplus官方文档中的代码生成配置

  • 方法二:插件mybatsx

  • 方法三:插件mybatisplus

P13 DB静态工具

iservice中的方法是非静态的,db方法是静态的。

静态方法无法读取到类的泛型的,也就无法知道实体类类型、表信息,可以看到方法中都需要传额外参数,即实体类的字节码,以得到相关信息。

例如:可能会存在两个service相互注入的情况,即循环依赖,如何解决?

使用静态工具进行解决,(静态工具和iservice用法差不多,就是额外传入字节码),但是这样能够避免注入service,避免了循环依赖。(当然也可以不适用iservice中的方法,也可以使用mapper中的方法)

示例:根据用户id查询用户信息,以及用户地址信息并返回;

@Override
    public UserVO queryUserAndAddressById(Long id){
        // 查询用户信息
        User user = getById(id);
        if (user == null || user.getStatus() == UserStatus.FROZEN){
            throw new RuntimeException("用户状态异常");
        }
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        // 查询用户地址,地址表一个用户多个地址
        // 使用lambdaquery,避免相互依赖,使用DB静态方法
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id)
                .list();
        if (CollUtil.isNotEmpty(addresses)){
            userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
        }
        return userVO; // 没有自己写sql就实现了业务操作
    }

P14 扩展功能-DB静态工具2

例如:实现批量查询用户,同时查询出用户地址信息。

注意查询地址信息,需要先获取用户的id集合,参数ids就是一个集合,如果以后再遇到这种,可以使用stream流从用户对象从获取到用户id的结合,例如:

List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());

@Override
    public List<UserVO> queryUserAndAddressByIds(List<Long> ids){
        //  查询用户
        List<User> users = listByIds(ids);
        if (CollUtil.isEmpty(users)){
            return Collections.emptyList();
        }
        // 方法参数传入的ids可能为空
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        // 查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .in(Address::getUserId, userIds)
                .list();
        // 转换地址VO
        List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
        // 将用户地址分组,
        Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
        if (CollUtil.isNotEmpty(addressVOList)){
            addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId)); // 依据用户id进行分组

        }
        // 转为VO返回
        List<UserVO> list = new ArrayList<>(users.size());
        for (User user : users){
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            list.add(userVO);
            userVO.setAddresses(addressMap.get(user.getId()));
        }
        return list;
    }

P15 扩展功能-逻辑删除

逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据

  • 在表中添加一个字段标记数据是否被删除

  • 当删除数据时置该字段为1

  • 查询时只查询标记为0的数据

这种功能mybatisplus已经提供了,直接帮我们在底层自动修改CRUD语句,在application.yaml中配置逻辑删除的字段名和值即可。

例如:

mybatis-plus:
  type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
  global-config:
    db-config:
      id-type: auto # 配置id生成的策略,如果是雪花算法就是assign_id
      logic-delete-field: deleted # 配置逻辑删除字段
      update-strategy: not_null # 更新策略,只更新非空字段

逻辑删除也会存在问题:

  • 垃圾数据越来越多

  • sql中全都需要对逻辑删除字段进行判断,影响查询效率

替代方案,采用数据迁移的方式

P16 扩展功能-枚举处理器

使用枚举类型。

问题:java中的枚举类型与数据库中的int类型相互转换问题。这个问题是mybatis实现的。

mybatisplus加入了几个类型处理器,就有MybatisEnumTypeHandler。

先配置:

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

加注解:

@EnumValue
private final int value; // 对应数据库中的值

我们定义的枚举类是UserStatus,修改用户实体类中的status的类型为这个枚举类型而不是int类型。

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

枚举返回前端默认是枚举项的名字,返回给前端的显示值,使用注解jsonvalue,可以自定义返回

@JsonValue
private final String desc;

P17 扩展功能-JSON处理器

mybatis plus还提供了AbstractJsonTypeHandler转换器,实现java和json的转换

例如数据库中info字段存储的json类型,如下图:

在这里插入图片描述

在java中使用这类数据,如果读取到再将字符串转换为可用的数据比较麻烦,可以考虑定义一个这种json格式的实体,然后使其变成这个对象,mybatis没有提供这个类型处理器,mybatis plus提供了。

在这里插入图片描述

例如:

  /**
     * 详细信息
     */
    @TableField(typeHandler = JacksonTypeHandler.class) // 定义类型处理器
    private UserInfo info;

还要开启自动的结果映射

@Data
@TableName(value = "user", autoResultMap = true) // 自动结果映射
public class User {

插件功能

mybatis plus提供的内置拦截器有:

在这里插入图片描述

P18 分页插件

之间使用了PageHelper,

使用mybatis plus中的分页插件,首先配置,

@Configuration
public class MyBatisConfig {

    @Bean // 声明一个bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建核心插件 ,可以往里面加插件
        // 1.创建分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);// 分页内置拦截器
        paginationInnerInterceptor.setMaxLimit(1000L);// 最多1000条
        // 2.添加分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor); // 可以添加插件
        return interceptor;
    }
}

使用分页的API,返回page结果

在这里插入图片描述

使用示例:

   @Test
    void testPageQuery() {
        int pageNo = 1, pageSize = 2;
        // 1.准备分页条件
        // 1.1.分页条件
        Page<User> page = Page.of(pageNo, pageSize); // 创建page对象
        // 1.2.排序条件
        page.addOrder(new OrderItem("balance", true));
        page.addOrder(new OrderItem("id", true));

        // 2.分页查询
        Page<User> p = userService.page(page);

        // 3.解析
        long total = p.getTotal();
        System.out.println("total = " + total);
        long pages = p.getPages();
        System.out.println("pages = " + pages);
        List<User> users = p.getRecords();
        users.forEach(System.out::println);
    }

P19 通用分页实体

例如:
在这里插入图片描述

从接收处理到业务逻辑实现到返回。

如果单独在UserDTO中实现,加入AddressDTO中也有分页需求,又要写。所以可以设计统一的分页查询请求,即公共分页类。

@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){
        // 1.分页条件
        Page<T> page = Page.of(pageNo, pageSize);
        // 2.排序条件
        if(StrUtil.isNotBlank(sortBy)){
            // 不为空
            page.addOrder(new OrderItem(sortBy, isAsc));
        }else if(items != null){
            // 为空,默认排序
            page.addOrder(items);
        }
        return page;
    }
    public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc){
        return toMpPage(new OrderItem(defaultSortBy, defaultAsc));
    }
    public <T> Page<T> toMpPageDefaultSortByCreateTime(){
        return toMpPage(new OrderItem("create_time", false));
    }
    public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
        return toMpPage(new OrderItem("update_time", false));
    }
}

userquery继承这个分页查询实体即可。

可以定义pageDTO,

@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Long total;
    @ApiModelProperty("总页数")
    private Long pages;
    @ApiModelProperty("集合")
    private List<T> list; // 泛型T,不知道具体哪种类型

    public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz){
        PageDTO<VO> dto = new PageDTO<>();
        // 1.总条数
        dto.setTotal(p.getTotal());
        // 2.总页数
        dto.setPages(p.getPages());
        // 3.当前页数据
        List<PO> records = p.getRecords();
        if (CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        // 4.拷贝user的VO
        dto.setList(BeanUtil.copyToList(records, clazz));
        // 5.返回
        return dto;
    }

    public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor){
        PageDTO<VO> dto = new PageDTO<>();
        // 1.总条数
        dto.setTotal(p.getTotal());
        // 2.总页数
        dto.setPages(p.getPages());
        // 3.当前页数据
        List<PO> records = p.getRecords();
        if (CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        // 4.拷贝user的VO
        dto.setList(records.stream().map(convertor).collect(Collectors.toList()));
        // 5.返回
        return dto;
    }
}