Mybatisplus基础入门笔记

Mybatisplus基础入门笔记

一、Mybatisplus

1、定义

  • mybatisplus用于解决对单表的增删改查,这些不需要业务处理的简单代码的代码集成。

2、使用

  1. 我们只需要在mapper接口去继承BaseMappper,在给一个要操作的实体类型就可以自动生成Mapper相应的增删改查,insert、delete、update、select。

  2. 相应的service层也提供了两个供我们继承的的接口以及实现类,Iservice和ServiceImpl<userMapper,User>。可以自动生成Service相应的增删改查,save、remove、update。获取有分为list、get。还有lambdaQuery和lambdaUpdate。

  3. 在继承的Service中自动注入了一个baseMapper,他就是我们在指定范型时传入的userMapper,所以我门可以在xxxSerciceImpl中使用baseMapper就可以调用对应的Mapper,当然想要调用其他xxxMapper就必须手动注入了。

@Mapper
public interface UserMapper extends BaseMapper<User> {
}
public interface IUserService extends IService<User> {
}
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
  
  /**
     * 删除一个用户
     * @param id
     */
  public void deleteById(Long id) {
    baseMapper.deleteById(id);
  }
}

3、条件构造器

  • 使用类型分为四个LambdaUpdateWrapper、LambdaQueryWrapper、UpdateWrapper、QueryWrapper

  • 用法:

    • QueryWrapperLambdaQueryWrapper主要主要是用来构建select、delete和update的where条件部分
    • UpdateWrapperLambdaUpdateWrapper通常只有在set语句比较特殊的时候才使用。
  • 推荐使用LambdaUpdateWrapper、LambdaQueryWrapper,避免硬编码,原理通过反射获取字段名。

    				List<Long> ids = List.of(1L,2L,3L);
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                    .select(User::getUsername, User::getBalance)
                    .in(User::getId,ids);
    
            List<User> userList = baseMapper.selectList(wrapper);
    

4、自定义SQL

  • 查询语句应该写到mapper层,写到service违反了三层架构的思想,所以我们采用wrapper加自定义Sql。

  • 使用方式,用wrapper在Service层写好where条件,将剩余操作值和wrapper一同传到mapper层。给wrapper用@Param标注,别名只能是“ew”,也可以使用MybatisPlus提供的Constants.WRAPPER

  • 在xxxmaper.xml文件上where部分使用${ew.customSqlSegment},来提取wrapper生成的where条件sql字段。

    		List<Long> ids = List.of(1L,2L,4L);
        int amount = 200;
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .in(User::getId,ids);
        userMapper.updateBalanceByIds(wrapper,amount);
    
    void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaQueryWrapper<User> wrapper,@Param("amount") int amount);
    
    <update id="updateBalanceByIds">
        update tb_user set balance = balance - #{amount} ${ew.customSqlSegment}
    </update>
    

5、IService批量新增注意事项(提高效率)

  • 在MybatisPlus中默认的批量新增,底层是将每一个对象都转化成一个sql执行语句,然后一次通信后全部挨个执行。如下所示

    insert into user(name,age,email) values('张三',18,zhangsan@qq.com);
    insert into user(name,age,email) values('李四',18,lisi@qq.com);
    insert into user(name,age,email) values('王五',18,wangwu@qq.com);
    
  • 优化方案:转化成一次执行语句添加多条数据,它是由mysql驱动控制的,只需要在yml文件的url连接上加一个参数&rewriteBatchedStatements=true。就可以转化成一次执行语句添加多条数据。如下所示

        url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    
    insert into user(name,age,email) 
      values('张三',18,zhangsan@qq.com),
     				('李四',18,lisi@qq.com),
            ('王五',18,wangwu@qq.com);
    

二、常用扩展功能

1、代码生成器

  • 定义:可以根据数据库的表,自动生成带有MybatisPlus功能的三层架构。
  • 推荐使用idea插件,插件市场搜索:MybatisPlus

2、DB静态工具

  • 定义:解决service互相调用而产生的循环依赖问题

  • 使用方法:和调用serviceImpl<userMapper,User>的方法是一样的,只不过在调用传一个对应数据类型就可以了。Db.lambdaQuery(Address.class)

  • 使用实例:

    @Service
    @Slf4j
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
      /**
         * 根据id查询用户和相关地址
         * @param id
         * @return
         */
        public UserVO queryUSerAndAddressById(Long id) {
            //根据id获取到用户
            User user = getById(id);
    
            //用户状态异常
            if(user == null || user.getStatus() == UserStatus.FROZEN){
                throw new RuntimeException("用户状态异常!");
            }
    
            //查询地址
            List<Address> addressList = Db.lambdaQuery(Address.class)
                    .eq(Address::getUserId, user.getId())
                    .list();
    
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
    
            //如果查询到的地址信息不为空,则添加地址信息
            if (CollUtil.isNotEmpty(addressList)){
                userVO.setAddressVOList(BeanUtil.copyToList(addressList, AddressVO.class));
            }
    
            return userVO;
        }
    }
    

3、逻辑删除

  • 定义:在数据库中可以设置一个逻辑删除的字段,设置开启值(默认0)和删除值(默认1)

  • 我们需要在yml文件开启mybatisplus的默认逻辑删除并指定逻辑删除字段,在删除信息时会将逻辑删除字段设置成删除值

    mybatis-plus:
    	  global-config:
          db-config:
            logic-delete-field: deleted
            logic-delete-value: 1 #逻辑删除值(默认为1)
            logic-not-delete-value: 0 #逻辑开启值(默认为0)
    

4、枚举处理器

  • 将对象的枚举值换成自定义枚举对象。

  • mybatis默认的Enum枚举处理器不能解决与数据库之间的转化,所以我们需要在yml文件中将默认的枚举转换器换成mybatisplus提供的枚举转换器MybatisEnumTypeHandler

    mybatis-plus:
      configuration:
        default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
    
  • 我们只需要告诉MybatisPlus将哪个字段作为值传入数据库,在对应字段加上注解@EnumValue。

  • @JsonValue可以在给前端传值时,指定要哪个为传的变量,将变量值作为内容传递。

    @Getter
    public enum UserStatus {
        NORMAL(1,"正常"),
        FROZEN(2,"冻结"),
        ;
    
        @EnumValue
        private final int value;
        @JsonValue
        private final String desc;
    
        UserStatus(int value, String desc) {
            this.value = value;
            this.desc = desc;
        }
    }
    

5、JSON处理器

  • 将对象转化成JSON存储到数据库。
  • mybatis默认使用的是JacksonTypeHandler,我们转换也用这个JSON处理器。
  • 使用两步操作:
    • 在要转换成json储存到数据库的成员变量上加注接@TableField(typeHandler = JacksonTypeHandler.class),指定JSON处理器为JacksonTypeHandler
    • 最后在需要转化的对象类上加上注解@TableName(value = “tb_user”,autoResultMap = true),主要是autoResultMap开启自动结果集映射
@Data
@TableName(value = "tb_user",autoResultMap = true)
public class User {

    //...字段省略

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

    //...字段省略
}

三、常用插件功能

1、分页查询插件

a、基本用法
  • 首先添加一个给容器添加MybatisPlusInterceptor,就可以开启分页查询插件了。

    @Configuration
    public class MybatisConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //创建分页查询
            PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            paginationInnerInterceptor.setMaxLimit(1000L);
            //添加分页查询
            interceptor.addInnerInterceptor(paginationInnerInterceptor);
    
            return interceptor;
        }
    }
    
  • 调用page方法,即可获得分页之后的数据。

    @Test
        void testPage(){
            int pageNo = 1;
            int pageSize = 3;
            //1、准备分页条件
            //1.1、排序规则
            Page<User> page = Page.of(pageNo, pageSize);
            //1.2、排序规则
            page.addOrder(OrderItem.asc("balance"));
            page.addOrder(OrderItem.asc("id"));
            //2、分页查询
            Page<User> p = userService.page(page);
    
            //3、解析
            //3.1、总条数
            long total = p.getTotal();
            System.out.println("total:"+total);
            //3.2、总页数
            long pages = p.getPages();
            System.out.println("pages:"+pages);
            //3.3、分页数据
            List<User> userList = p.getRecords();
            userList.forEach(System.out::println);
        }
    
b、通用分页实体
  • 我们可以事先准备一个通用的分页实体,当其他实体需要分页查询时继承通用的分页实体即可

    @Data
    @ApiModel("分页结果")
    public class PageDto<T> {
        @ApiModelProperty("总条数")
        private Long total;
        @ApiModelProperty("总页数")
        private Long pages;
        @ApiModelProperty("查询的数据集合")
        private List<T> list;
    }
    
c、分页查询公共代码封装成工具
  • PageQuery

    @Data
    @ApiModel("分页查询实体")
    public class PageQuery {
        @ApiModelProperty("页码")
        private Integer pageNo;
        @ApiModelProperty("分页条数")
        private Integer pageSize;
        @ApiModelProperty("排序字段")
        private String sortBy;
        @ApiModelProperty("是否升序")
        private Boolean isAsc = true;
    
        public <T> Page<T> toMpPage(OrderItem ...items){
            //1.1、分页条件
            Page<T> page = Page.of(pageNo, pageSize);
            //1.2、排序条件
            if (StrUtil.isNotBlank(sortBy)){
                //排序字段不为空,则安照传入字段排序
                page.addOrder(new OrderItem().setColumn(sortBy).setAsc(isAsc));
            } else if(items != null){
                //排序字段为空,则安照更新时间排序
                page.addOrder(items);
            }
    
            return page;
        }
    
        public <T> Page<T> toMpPage(String defaultSortBy,Boolean defaultAsc){
            return toMpPage(new OrderItem().setColumn(defaultSortBy).setAsc(defaultAsc));
        }
    
        public <T> Page<T> toMpPageDefaultSortByCreateTime(){
            return toMpPage(new OrderItem().setColumn("create_time").setAsc(false));
        }
    
        public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
            return toMpPage(new OrderItem().setColumn("update_time").setAsc(false));
        }
    }
    
  • PageDto

    @Data
    @ApiModel("分页结果")
    public class PageDto<T> {
        @ApiModelProperty("总条数")
        private Long total;
        @ApiModelProperty("总页数")
        private Long pages;
        @ApiModelProperty("查询的数据集合")
        private List<T> list;
    
        public static <PO,VO> PageDto<VO> of(Page<PO> p,Class<VO> clazz){
            PageDto<VO> pageDto = new PageDto<>();
            //1、总条数
            pageDto.setTotal(p.getTotal());
            //2、总页数
            pageDto.setPages(p.getPages());
            //3、当前页数据
            List<PO> userPageList = p.getRecords();
            if (CollUtil.isEmpty(userPageList)){
                pageDto.setList(Collections.emptyList());
                return pageDto;
            }
            //4、拷贝user的VO
            pageDto.setList(BeanUtil.copyToList(userPageList, clazz));
    
            return pageDto;
        }
    
        public static <PO,VO> PageDto<VO> of(Page<PO> p, Function<PO,VO> convertor){
            PageDto<VO> pageDto = new PageDto<>();
            //1、总条数
            pageDto.setTotal(p.getTotal());
            //2、总页数
            pageDto.setPages(p.getPages());
            //3、当前页数据
            List<PO> userPageList = p.getRecords();
            if (CollUtil.isEmpty(userPageList)){
                pageDto.setList(Collections.emptyList());
                return pageDto;
            }
            //4、拷贝user的VO
            pageDto.setList(userPageList.stream().map(convertor).collect(Collectors.toList()));
    
            return pageDto;
        }
    }
    
  • 最后在sercice层将工具调用

    /**
         * 条件分页查询用户
         * @param query 将查询条件封装成一个对象
         * @return PageDto<User>
         */
        public PageDto<UserVO> queryUsersPage(UserQuery query) {
            String name = query.getName();
            UserStatus status = query.getStatus();
    
            //1、构建查询条件
            Page<User> page = query.toMpPageDefaultSortByUpdateTime();
    
            //2、分页查询
            Page<User> p = lambdaQuery()
                    .like(name != null, User::getUsername, name)
                    .eq(status != null, User::getStatus, status)
                    .page(page);
    
            //3、封装VO结果
            //4、返回
            return PageDto.of(p,user -> {
                //基础拷贝
                UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
                //其他特殊操作
                userVO.setUsername(userVO.getUsername().substring(0,userVO.getUsername().length() - 2) + "**");
    
                return userVO;
            });
        }
    
  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值