MyBatisPlus——3 扩展功能

3 扩展功能

3.1 代码生成

  • 在使用MybatisPlus以后,基础的MapperServicePO代码相对固定,重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成POMapperService等相关代码。只不过代码生成器同样要编码使用,也很麻烦;
  • 这里推荐使用一款MybatisPlus的插件,它可以基于图形化界面完成MybatisPlus的代码生成,非常简单。
3.1.1 安装插件

在这里插入图片描述

  • 重启IDEA。
3.1.2 使用
  • 配置数据库地址,在Idea顶部菜单中,找到other,选择Config Database

    在这里插入图片描述

  • 在弹出的窗口中填写数据库连接的基本信息:

    在这里插入图片描述

  • 点击test connect测试连接。若报时区错误,则参考该文章MySQL如何修改时区_servertimezone=北京时间-优快云博客

  • 点击OK保存。然后再次点击Idea顶部菜单中的other,然后选择Code Generator

    在这里插入图片描述

  • 在弹出的表中填写信息:

在这里插入图片描述

3.2 静态工具

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

    在这里插入图片描述

  • 需求:

    • 改造根据id查询用户的接口,查询用户的同时,查询出用户对应的所有地址;

      1. 添加一个收货地址的VO对象:

        package com.shisan.mp.domain.vo;
        
        import io.swagger.annotations.ApiModel;
        import io.swagger.annotations.ApiModelProperty;
        import lombok.Data;
        
        @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;
        }
        
      2. 改造原来的UserVO,添加一个地址属性:

        在这里插入图片描述

      3. 修改UserController中根据id查询用户的业务接口:

        @ApiOperation("根据id查询用户接口")
        @GetMapping("{id}")
        public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
            return userService.queryUserAndAddressById(id);
        }
        
      4. 在IUserService中定义方法:

        UserVO queryUserAndAddressById(Long id);
        
      5. 在UserServiceImpl中实现该方法:

        @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
            //转User的PO为VO
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            //转地址的PO为VO
            if(CollUtil.isNotEmpty(addresses)){ //如果地址不为空
                userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
            }
            return userVO;
        }
        
      6. 在查询地址时,采用了Db的静态方法,因此避免了注入AddressService,减少了循环依赖的风险;

    • 改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址;

      1. 修改UserController中根据id批量查询用户接口:

        @ApiOperation("根据id批量查询用户接口")
        @GetMapping
        public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
            return userService.queryUserAndAddressByIds(ids);
        }
        
      2. 在IUserService中定义方法:

        List<UserVO> queryUserAndAddressByIds(List<Long> ids);
        
      3. 在UserServiceImpl中实现该方法:

        @Override
        public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {
            //1、查询用户
            List<User> users = listByIds(ids);
            if(CollUtil.isEmpty(users)){
                return Collections.emptyList();
            }
            //2、查询地址
            //获取用户id
            List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
            //根据用户id查询地址
            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));
            }
            //3、转VO返回
            List<UserVO> list = new ArrayList<>(users.size());
            for (User user : users) {
                //转换User的VO为PO
                UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
                list.add(vo);
                //转换地址VO
                vo.setAddresses(addressMap.get(user.getId()));
            }
        
            return list;
        }
        

3.3 逻辑删除

  • 对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:

    • 在表中添加一个字段标记数据是否被删除;
    • 当删除数据时把标记置为true(即已删除);
    • 查询时过滤掉标记为true的数据(即查询标记为false的数据,未被删除的数据);
  • 一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持;

    • 注意:只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除;
  • MvbatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

    在这里插入图片描述

  • 例:

    1. address表添加一个逻辑删除字段:

      alter table address add deleted bit default b'0' null comment '逻辑删除';
      
    2. Address实体添加deleted字段:

    在这里插入图片描述

    1. application.yml中配置逻辑删除字段:

      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)
      
    2. 执行删除和查询操作:

      @Test
      void testLogicDelete() {
          //删除
          addressService.removeById(59);
          //查询
          Address address = addressService.getById(59);
          System.out.println(address);
      }
      
    3. 方法与普通删除一模一样,但是底层的SQL逻辑变了。且会发现id为59的确实没有查询出来,而且SQL中也对逻辑删除字段做了判断:

      在这里插入图片描述

    4. 综上, 开启了逻辑删除功能以后,就可以像普通删除一样做CRUD,基本不用考虑代码逻辑问题。还是非常方便的。

  • 注意:

    • 逻辑删除本身也有自己的问题,比如:
      • 会导致数据库表垃圾数据越来越多,从而影响查询效率;
      • SQL中全都需要对逻辑删除字段做判断,影响查询效率;
    • 因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。

3.4 枚举处理器

  • User类中有一个用户状态字段:

    在这里插入图片描述

  • 像这种字段一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是数据库采用的是int类型,对应的PO也是Integer。因此业务操作时必须手动把枚举Integer转换,非常麻烦;

  • 因此,MybatisPlus提供了一个处理枚举的类型转换器,可以把枚举类型与数据库类型自动转换

3.4.1 定义枚举
  • 定义一个用户状态的枚举:

    在这里插入图片描述

  • 代码如下:

    package com.shisan.mp.enums;
    
    import com.baomidou.mybatisplus.annotation.EnumValue;
    import lombok.Getter;
    
    @Getter
    public enum UserStatus {
        NORMAL(1, "正常"),
        FREEZE(2, "冻结")
        ;
        private final int value;
        private final String desc;
    
        UserStatus(int value, String desc) {
            this.value = value;
            this.desc = desc;
        }
    }
    
  • User类中的status字段改为UserStatus 类型(UserVO中也要改):

    在这里插入图片描述

  • 要让MybatisPlus处理枚举与数据库类型自动转换,必须告诉MybatisPlus,枚举中的哪个字段的值作为数据库值。 MybatisPlus提供了@EnumValue注解来标记枚举属性:

    在这里插入图片描述

  • 后端给前端默认返回枚举变量的名称,即NORMALFREEZE。可以在字段上加上@JsonValue注解,使后端返回对应的枚举变量的字段,此处返回正常冻结

    在这里插入图片描述

3.4.2 配置枚举处理器
  • 在application.yaml文件中添加配置:

    mybatis-plus:
      configuration:
        default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
    
3.4.3 测试
  • 修改UserServiceImpl.java:

    在这里插入图片描述

3.5 JSON处理器

  • 数据库的user表中有一个info字段,是JSON类型:

    在这里插入图片描述

  • 格式像这样:

    {"age": 20, "intro": "佛系青年", "gender": "male"}
    
  • 而目前User实体类中却是String类型:

    在这里插入图片描述

  • 要读取info中的属性时就非常不方便。如果要方便获取,info的类型最好是一个Map或者实体类;

  • 而一旦把info改为对象类型,就需要在写入数据库时手动转为String,再读取数据库时,手动转换为对象,这会非常麻烦;

  • 因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用JacksonTypeHandler处理器。

3.5.1 定义实体
  • 定义一个单独实体类来与info字段的属性匹配:

    在这里插入图片描述

  • 代码如下:

    package com.shisan.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;
    }
    
3.5.2 使用类型处理器
  • 开启自动结果集映射,即自动映射实体类和数据库字段的映射关系:

    在这里插入图片描述

  • 将User类的info字段修改为UserInfo类型,并声明类型处理器(UserVO中也要改,但是无需声明类型处理器):

    在这里插入图片描述

4 插件功能

  • MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:

    • PaginationInnerInterceptor:自动分页
    • TenantLineInnerInterceptor:多租户
    • DynamicTableNameInnerInterceptor:动态表名
    • OptimisticLockerInnerInterceptor:乐观锁
    • IllegalSQLInnerInterceptor:sql 性能规范
    • BlockAttackInnerInterceptor:防止全表更新与删除
  • 注意: 使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:

    • 多租户,动态表名
    • 分页,乐观锁
    • sql 性能规范,防止全表更新与删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失散13

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值