Bootstrap

MybatisPlus总结

目录

一、快速入门

二、核心功能

三、扩展功能

四、插件功能

五、MybatisPlus追问巩固


前言

1. MybatisPlus完全支持Mybatis的功能,只是作为Mybatis的补充而非替代

2. MybatisPlus的优势是在单表查询,可以极大的简化代码逻辑的书写,利用提供的方法直接完成

3. Lambda表达式特别好用

4. 润物细无声,单表效率至上

MyBatis-Plus 🚀 为简化开发而生

一、快速入门

1.1 入门案例 & 使用步骤说明

1. 引入Mybatis起步依赖替代Mybatis依赖

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

2. 定义Mapper接口继承BaseMapper【继承之后才能使用MP相关的方法】

public interface UserMapper extends BaseMapper<User> {

    // 有必要讲一下:BaseMapper<User>中的User是使用MP对应的实体类。
    // MP通过你指定好的实体类进行扫描获取相应的属性,才知道对应的方法要怎么去完成

}

3. 入门案例展示:

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

userMapper.insert(user);
User user = userMapper.selectById(5L);
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
userMapper.updateById(user);
userMapper.deleteById(5L);

1.2 常见注解

mp映射的实体类的规范:

  1. 类名驼峰转下划线作为表名
  2. 名为id的字段作为主键
  3. 变量名驼峰转下划线作为表的字段名

mp常见的作用于实体类对象上的注解:

  1. @TableName:指定表名
  2. @TableId:指定表中的主键字段
  3. @TableFieId:指定表中的普通字段

【注意】

使用@TableField的常见场景:

  1. 成员变量名与数据库字段名不一致 例如 username 和 name
  2. 成员变量名以is开头,且是布尔值  例如 is_married 和 isMarried
  3. 成员变量名与数据库关键字冲突  例如 order 
  4. 成员变量不是数据库字段 

IdType的常见类型:AUTO(自增)、ASSIGN_ID(雪花算法)、INPUT(set输入)

1.3 常见配置

MyBatisPlus中的配置大都是默认配置好的,我们使用的时候基本不用修改大量的配置,除非遇到特殊的情况需要设置一些配置,可以参考MyBatisPlus的官方文档进行修改。也就是约定大于配置。

二、核心功能

2.1 条件构造器

【扩展阅读推荐】条件构造器AbstractWrapper详解-CSDN博客

【是什么、为什么】在简单CRUD操作中,使用MP BaseMapper中的泛型方法可以从容应对。然而对于复杂的需求是,往往需要携带各种条件参数进行CRUD,MybatisPlus的条件构造器就是用于编写复杂的SQL语句。

观察 BaseMapper,除了泛型参数外,还有一个Wrapper参数,专门是用于传递参数条件的。

对于Wrapper类进行说明:

展开看AbstractWrapper抽象基类:

展开看QueryWrapper子类:

扩展看UodateWrapper子类:

2.1.2 基于QueryWrapper的条件查询 & Lambda扩展优化

需求: 查询出名字中带o的,存款大于等于1000元的人的id、username、info、balance字段

2.1.2 基于UpdateWrapper的条件查询 & Lambda扩展优化

需求:更新id为1,2,4的用户的余额,扣200

2.1.3 总结

1. QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分

2. UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用

3. 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码问题

2.2 自定义SQL

为什么要用自定义SQL,结合XML + MP的相关优势。对于简单部分,我们可以直接用Mybatis提供的部分,而对于复杂的条件部分,如果还使用XML来做,代码就显得十分冗杂。因此在where部分,我们可以通过MP去提供。从而形成相对简单的SQL语句书写方式。

【基本步骤】

1. 基于Wrapper构建where条件

2. 在mapper方法参数中用Param注解声明wrapper变量名称必须是ew【底层定义好的】

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

2.3 MP的Service接口

前面介绍的MP方法都是作用在Mapper层的,而我们的业务代码逻辑通常会涉及到Service层上。于是MP在Service层上也实现了一个封装若干方法的IService的接口,用于实现基本的业务逻辑。

2.3.1 实现Service接口的基本流程

  1. 首先需要你的Service继承MP实现的IService接口
  2. 其次为了在实现类中简化实现,需要你的ServiceImpl实现Service接口的同时,继承MP中的ServiceImpl。

2.3.2 Service接口方法的使用

简单方法直接调用方法解决,复杂方法才需要在业务层进行拆分

@RestController
@RequestMapping("/user")
public class UserComtroller {

    @Resource
    private UserService userService;

    /**
     * 新增用户
     */
    @PostMapping()
    @ApiOperation(value = "新增用户")
    public void addUser(@RequestBody UserFormDTO userDTO) {
        // 1. DTO转PO
        User user = BeanUtil.copyProperties(userDTO, User.class);
        //2. 调用新增方法
        userService.save(user);
    }

    /**
     * 删除用户
     */
    @DeleteMapping("{id}")
    public void deleteUser(@PathVariable( "id") Long id) {
        userService.removeById(id);
    }

    /**
     * 根据io查询用户
     */
    @GetMapping("{id")
    public UserVO getUserById (@PathVariable("id") Long id) {
        User u = userService.getById(id);
        // PO转VO
        UserVO userVO = BeanUtil.copyProperties(u, UserVO.class);
        return userVO;
    }

    /**
     * 根据id批量查询用户
     */
    @GetMapping()
    public List<UserVO> getUserListByIds(@RequestParam List<Long> ids) {
        List<User> users = userService.listByIds(ids);
        // PO转VO
        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
        return userVOS;
    }

    /**
     * 根据id扣减余额
     *
     */
    @PutMapping("/{id}/deduction/{money}")
    public void deductionMoney(@PathParam("id") Long id, @PathParam("money") Integer money) {
        userService.deductionMoney(id, money);
    }
}




/*--------------------ServiceImpl-------------------------*/
/**
     * 扣减金额
     * @param id
     * @param money
     */
    @Override
    public void deductionMoney(Long id, Integer money) {
        // 首先思考Service接口有没有方法,有就直接调自己

        // 1. 根据id查询用户
        User user = getById(id);
        if(user == null){
            throw new RuntimeException("用户不存在");
        }

        //2. 判断用户余额是否足够
        if(user.getBalance() < money){
            throw new RuntimeException("余额不足");
        }

        //3. 执行扣减语句
        user.setBalance(user.getBalance() - money);
        updateById(user);
    }

2.3.3 Service接口的Lambda查询使用

/**
     * 复杂条件查询 使用Lambda
     * @param userQDTO
     */
    @Override
    public List<UserVO> getUserList(UserQueryDTO userQDTO) {
        String name = userQDTO.getUserName();
        Integer status = userQDTO.getStatus();
        Integer minBalance = userQDTO.getMinBalance();
        Integer maxBalance = userQDTO.getMaxBalance();

        List<User> userList = 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();

        List<UserVO> userVOS = BeanUtil.copyToList(userList, UserVO.class);
        return userVOS;
    }

2.3.4 Service接口的Lambad修改使用

需求:

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

  1. 完成对用户状态校验
  2. 完成对用户余额校验
  3. 如果扣减后余额为0,则将用户status修改为冻结状态(2)

注意:这里的乐观锁实际上利用了 CAS法。在进行更新前,检查要更新的金额是不是和查询到的相同,如果相同表明当前金额没有被别人修改,可以放心修改,这就是乐观策略

2.3.5 Service接口的Lambda批量新增使用及批处理方案分析

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

1. 普通for循环插入

10万条SQL语句、10万次提交事务请求 

2. IService的批量插入

以1000次为批量插入,10万条SQL语句,100次提交事务请求,大大减少网络请求,提高了效率

3. 开启rewriteBatchedStatements=true参数

以1000次为批量插入,重写成100条SQL语句,100次提交事务请求,大大减少网络请求,提高了效率

总结:

三、扩展功能

3.1 代码生成

就是借助工具把基本的架子生成,常见的工具有MybatisX、Mybatis【IDEA插件】

演示Mybatis插件:

3.2 DB静态工具

DB静态工具类也是提供了一系列CRUD的方法,与Service接口不同的是,由于静态工具类方法无法直接获取类的对象,需要我们使用者手动传递对象的字节码。通过反射来获取对象的属性,正如方法中的T泛型。

为什么要用DB静态工具类?

DB的基本语法使用:和Service基本一致

3.3 逻辑删除

逻辑删除就是模拟删除效果,但实际上并不会将数据库中的数据删除掉。对于一些需要进行统计、或者安全性较高的场景下,我们常常需要使用逻辑删除而非物理删除。

常见的逻辑删除策略就是在数据库表中添加一个逻辑字段 deleted(1:删除,0:没删除),用于标记数据是否删除。当用户发送删除请求是,此时不再执行Delete语句,而是执行Update语句。将deleted 置为 1

具体在实现的过程中,我们并不用关系修改语句实现,只需要通过配置MybatisPlus,就能让MP帮我们在底层将逻辑功能删除实现:

3.4 枚举处理器

在比较赋值的时候,当状态多了,单单通过数字1、2、3、4来比较很容易出错。因此我们可以利用枚举类型将比较赋值的方式规范化。从而提高代码的可阅读性和可维护性。

 如何使用枚举类型处理器

1. 创建枚举类,给枚举中的与数据库对应value值添加@EnumValue注解

2. 在配置文件中配置统一的枚举处理器,实现类型转换

3. 使用情况:

3.5 JSON处理器

有时候,如果设计的数据库字段值有一个字段是JSON类型的。在后端交付数据给前端的时候,我们往往需要预处理好再交给前端。

我们使用一个UserInfo对象来描述JSON字段。使用MP的JSON处理器就可以实现上述的映射转换:

JSON使用步骤——基于注解 

四、插件功能

4.1 分页插件介绍

MP也提供一个分页插件,我觉得实际上就是将PageHelper给实现了一下,底层也是基于拦截器去做的。

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

@Configuration
//MybatisPlus配置类
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1. 初始化核心插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //2. 添加分页插件 指定数据库类型
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //3. 设置分页参数
        paginationInnerInterceptor.setMaxLimit(1000L);
        //4. 将分页插件添加到核心插件中
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}

2. 构建分页对象参数,然后使用分页方法就行了

    @Test
    void testMPPageQuery() {
        //1. 构建分页条件
        int pageCurrent = 1;
        int pageSize = 2;
        Page<User> page = Page.of(pageCurrent, pageSize);

        // 2. 构建查询条件
        LambdaQueryWrapper<User> wrapper= new LambdaQueryWrapper<User>()
                .like(User::getUsername,"o")
                .ge(User::getBalance,1000);

        //3. 执行查询,传入page对象
        userMapper.selectPage(page,wrapper);
    }

五、MybatisPlus追问巩固

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

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

3. MybatisPlus的常用注解有哪些?

4. IdType的常见类型有哪些?

5. 使用@TableField的常见场景是?

6. 什么是条件构造器?为什么要有条件构造器?

7. Wrapper类有哪些常见的子类?分别有哪些特点?

8. LambdaQueryMrapper 和 LambdaUpdateMrapper 特点是是什么?

9. 为什么MP还要有自定义SQL?

10. Service接口和 Mapper接口有何区别?为什么还要有一个继承了MP封装方法的Service接口?

11. 请你概述一下MP的Service接口的基本使用流程【注意事项】

12. 简单谈一下你对批量插入方案的看法,如何提高批量插入的效率?

13. 为什么要有DB静态工具,DB静态工具的使用场景是什么?

14. DB静态工具相对于Service接口的方法特点是什么?

15. 删除策略——介绍一下逻辑删除的必要性和使用场景?

16. 逻辑删除策略的实现方案及其比较?

17. 如何实现PO类中的枚举类型变量与数据库字段的转换?

18. MP中如何实现将JSON类型字段转Java对象?

19.MP分页插件的使用步骤?

;