MyBatis-Plus

  • MyBatis它是一个非常流行的持久层框架,主要是用来做数据库的增删改查的。
  • MyBatis-Plus就是对于MyBatis的一个增强和升级,需要注意的是MyBatis-Plus并不是来替代MyBatis这个技术的,两者之间是一种合作的关系。

MyBatis-Plus_第1张图片

MyBatis-Plus的官方网站: 

MyBatis-PlusMyBatis-Plus 官方文档icon-default.png?t=N7T8https://baomidou.com/MyBatis-Plus_第2张图片 

MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变(无侵入,不修改源代码也无妨),为简化开发,提高效率而生。

MyBatis-Plus的愿景是成为MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍!

MyBatis-Plus_第3张图片

大家在日常开发当中应该能够发现,单表的CRUD功能代码重复度很高,也没有什么难度,而这部分代码量往往比较大,开发起来比较费时。

因此,目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作,目前在国内使用较多的一个组件就是MyBatis-Plus。 

当然,MyBatis-Plus不仅仅可以简化单表的操作,而且还对MyBatis的功能有很多的增强,可以让我们的开发更加的简单、高效。

学习目标:
  • 能利用MyBatis-Plus实现基本的CRUD
  • 会使用条件构建查询和更新语句
  • 会使用MyBatis-Plus中的常用注解
  • 会使用MyBatis-Plus处理枚举、JSON类型的字段
  • 会使用MyBatis-Plus实现分页 

1. 快速入门

我们要实现单表的CRUD,只需要下面几步:

  • 引入MyBatis-Plus的起步依赖
  • 自定义的Mapper接口需要继承MyBatis-Plus提供的BaseMapper接口 

1.1 引入MyBatis-Plus的起步依赖,代替MyBatis依赖 

MyBatis-Plus官方提供了starter,其中集成了MyBatis和MyBatis-Plus的所有功能,并且实现了自动装配效果。

因此我们可以用MyBatis-Plus的starter代替MyBatis的starter:



    com.baomidou
    mybatis-plus-boot-starter
    3.5.3.1

application.yml配置文件: 

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
logging:
  level:
    com.itheima: debug
  pattern:
    dateformat: HH:mm:ss
mybatis:
  mapper-locations: classpath*:mapper/*.xml

1.2  自定义的Mapper接口需要继承MyBatis-Plus提供的BaseMapper接口(泛型接口)

MyBatis-Plus_第4张图片

  • 指定泛型接口BaseMapper的泛型类型为你要操作的实体类的类型~! 

为了简化单表操作的CRUD,MyBatis-Plus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD:

MyBatis-Plus_第5张图片

MyBatis-Plus_第6张图片MyBatis-Plus_第7张图片以后操作就不用再自定义mapper.xml映射文件写一大堆语句了,直接调API即可,会自动生成对应的SQL语句! 

所以MyBatis它可以帮助我们自动生成SQL语句实现单表的增删改查只需要继承BaseMapper就能省去所有的单表的CRUD,是不是非常简单~! 

1.1 常见注解

在刚才的入门案例当中,我们仅仅引入了依赖,继承了BaseMapper就能使用MyBatis-Plus,非常简单,但是问题来了,MyBatis-Plus如何知道我们要查询的是哪张表,表中有哪些字段呢? 

回忆,自定义的UserMapper接口在继承BaseMapper接口时指定了一个泛型,也就是我们对应实体类的类型:

MyBatis-Plus_第8张图片泛型接口BaseMapper所指定的泛型中的User类型就是与数据库对应的PO(POJO)实体类的类型:

MyBatis-Plus帮我们自动实现增删改查基本原理:

MyBatis-Plus通过扫描PO实体类,并基于反射获取实体类信息作为数据表信息,从而生成增删改查的SQL语句,默认情况下是有一些约定的:

  • MyBatis-Plus会把PO实体类的类名驼峰转下划线作为表名
  • MyBatis-Plus会把名为id的字段作为主键
  • MyBatis-Plus会把PO实体类的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型 

但很多情况下,如果我们的实体类不符合这些约定,默认的实现与实际场景不符因此MyBatis-Plus提供了一些注解便于我们声明表信息:

MyBatis-Plus当中比较常用的几个注解如下:

  • @TableName:用来指定表名
  • @TableId:用来指定表中的主键字段信息
  • @TableField:用来指定表中的普通字段信息

如果实体类与表中字段的对应关系不符合MP的约定,此时就需要用到上面的注解来去标记了! 

@TableName注解

说明:

  • 描述:表名注解,标识实体类对应的表名
  • 使用位置:实体类,通过value属性指定真实的表名称,这样MP就可以利用反射找到它的真实表名,从而去实现增删改查的SQL了!

@TableId注解

说明:

  • 描述:主键注解,标识实体类中的主键字段
  • 使用位置:实体类的主键字段 

@TableId注解支持两个属性:

MyBatis-Plus_第9张图片IdType支持的枚举类型有:

MyBatis-Plus_第10张图片MyBatis-Plus_第11张图片

MyBatis-Plus_第12张图片

MyBatis-Plus_第13张图片

@TableField注解

说明:

  • 描述:普通字段注解

一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外,使用@TableFileld的常见场景:

  • 成员变量名与数据库字段名不一致
  • 成员变量是以isXXX开头命名,且是布尔值,按照JavaBean的规范,MyBatis-Plus识别字段时会把is去除,这就导致与数据库不符(Alibaba开发规约当中也有此说明)
  • 成员变量名与数据库表中字段名一致,但是成员变量名与数据库的关键字冲突,使用@TableField注解给字段名添加转义字符,比如成员变量名为order,同时order也是MySQL数据库党徽中的关键字(Order By)
  • 成员变量根本就不是数据库字段
@TableField支持的其它属性如下:

MyBatis-Plus_第14张图片

MyBatis-Plus_第15张图片

1.2 常见配置 

MyBatis-Plus也支持基于yaml文件的自定义配置,详见官方文档:
使用配置 | MyBatis-PlusMyBatis-Plus 官方文档icon-default.png?t=N7T8https://www.baomidou.com/pages/56bac0/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AEMyBatis-Plus的配置项集成了MyBatis的原生配置和一些自己特有的配置,例如:

MyBatis的原生配置: 

  • 实体类(型)的别名扫描包:需要指定对应的po实体类的包名,将来就会扫描到所有的PO实体类,它的作用就是将来在定义mapper.xml文件中的一些SQL语句的时候,如果需要定义实体类的类型,就不需要写全路径名了,直接写类名即可!
  • mapper.xml文件的读取地址:需要注意的是,MyBatis-Plus也支持手写SQL的,而mapper文件的读取地址可以自己配置,MP它更擅长的是单表的增删改查,如果说SQL语句比较复杂或者是多表的查询,其实还是需要去手写SQL语句的,因此MP也允许去手动指定mapper.xml的文件地址,允许你去自定义SQL。
  • 是否开启下划线和驼峰的映射:开启
  • 是否开启二级缓存:一般情况下二级缓存我们是不需要开启的!

MyBatis-Plus的特有配置:

global-config:全局配置

  • id-type:全局id的类型
  • update-strategy:更新策略

全局配置的优先级是不如注解的优先级高的~! 

上面大多数的配置都有默认值,因此我们都无需配置,但还有一些是没有默认值的,例如:

  • 实体类的别名扫描包
  • 全局id类型 
  • mapper文件的读取地址也可以自己配置,MyBatis-Plus支持手写SQL
  mybatis-plus:
    type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
    mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
    configuration:
      map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
      cache-enabled: false # 是否开启二级缓存
    global-config:
      db-config:
        id-type: assign_id # id为雪花算法生成
        update-strategy: not_null # 更新策略:只更新非空字段

MyBatis-Plus使用的基本流程:

  1. 引入起步依赖
  2. 自定义Mapper接口继承BaseMapper
  3. 如果实体类定义没有遵循约定,则在实体类上添加注解声明表信息
  4. 在application.yml中根据需要添加配置 

2. 核心功能

  • 刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了。 

2.1 条件构造器 - Wrapper

  • 除了新增以外,修改、删除、查询的SQL语句都需要指定where条件,因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。 

MyBatis-Plus支持各种复杂的where条件,可以满足日常开发的所有需求! 

利用Wrapper来实现复杂查询~! 

MyBatis-Plus_第16张图片

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

MyBatis-Plus_第17张图片Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

MyBatis-Plus_第18张图片

  • eq:等于
  • ne:不等于
  • gt:大于
  • ge:大于等于 

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

MyBatis-Plus_第19张图片而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

MyBatis-Plus_第20张图片

1. 基于QueryWrapper的查询

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件,接下来看一些例子:

查询:查询出名字username中带o的,存储balance大于等于1000元的人的id、username、info、balance字段,代码如下: 

select id, username, info, balance
from table
where username like '%o%'
  and balance >= 1000;
    /**
     * 条件查询
     * select id,username,info,balance where username like '%o%' and balance >= 1000;
     */
    @Test
    void testQueryWrapper() {
        // 1.创建QueryWrapper对象
        QueryWrapper wrapper = new QueryWrapper<>();
        // 2.链式编程,构建查询条件
        wrapper.select("id", "username", "info", "balance")
                .like("username", "o")
                .ge("balance", 2000);
        // 3.查询数据
        List userList = userMapper.selectList(wrapper);
        // 4.方法引用遍历输出查询返回的List集合
        userList.forEach(System.out::println);
    }

2. 基于UpdateWrapper的更新

更新:更新用户名为jack的用户的余额为2000 

update table
set balance = 2000
where (username = 'jack');
    /**
     * 更新语句
     * update table set balance = 2000 where username = 'jack';
     */
    @Test
    void testUpdateWrapper() {
        // 1.创建UpdateWrapper对象
        UpdateWrapper updateWrapper = new UpdateWrapper<>();
        // 2.链式编程,构建更新条件
        updateWrapper.eq("username","jack");
        // 3.更新数据
        User u = new User();
        u.setBalance(528);
        // 4.执行更新:userMapper.update(要更新的数据,更新的where条件)
        userMapper.update(u,updateWrapper);
    }

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

update table
set balance = balance - 200
where id in (1, 2, 4);

基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。

MyBatis-Plus_第21张图片

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:

    /**
     * 更新语句
     * update table set balance = balance - 200 where id in(1,2,4);
     */
    @Test
    void testUpdateWrapper() {
        // 1.创建UpdateWrapper对象
        UpdateWrapper updateWrapper = new UpdateWrapper<>();
        // 2.链式编程,构建更新条件
        updateWrapper.setSql("balance = balance - 200") // set balance = balance - 200
                        .in("id",List.of(1L,2L,4L)); // where id in(1,2,4)
        // 3.执行更新 userMapper.update(要更新的数据,更新的where条件)
        // 注意:第一个参数可以传null,.也就是不填更新字段和数据,而是基于UpdateWrapper中的setSql来更新
        userMapper.update(null,updateWrapper);
    }

3. LambdaQueryWrapper[推荐用法]

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称 - 属于硬编码,会出现字符串魔法值,这在编程规范中显然是不推荐的。 

LambdaQueryWrapper和QueryWrapper以及UpdateWrapper在功能上基本是一样的! 

区别:LambdaQueryWrapper在构建条件时是基于Lambda语法! 

那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的getter方法结合反射技术因此我们只要将条件对应的字段的getter方法传递给MyBatis-Plus,它就能计算出对应的变量名了,而传递方法可以使用JDK8中的方法引用和Lambda表达式。 

因此MyBatis-Plus又提供了一套基于Lambda的Wrapper,包含两个:
  • LambdaQueryWrapper
  • LambdaUpdateWrapper 

分别对应QueryWrapper和UpdateWrapper

尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码! 

LambdaQueryWrapper

    /**
     * 条件查询之LambdaQueryWrapper‘
     * select id,username,info,balance where username like '%o%' and balance >= 1000;
     */
    @Test
    void testLambdaQueryWrapper() {
        // 1.创建QueryWrapper对象
//      new QueryWrapper<>().lambda() <=> new LambdaQueryWrapper<>()
        LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 2.链式编程,构建查询条件,方法参数传对应字段的get函数
        lambdaQueryWrapper
                .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                .like(User::getUsername, "o")
                .ge(User::getBalance, 1000);
        // 3.查询数据
        List userList = userMapper.selectList(lambdaQueryWrapper);
        // 4.方法引用遍历输出查询返回的List集合
        userList.forEach(System.out::println);
    }

MyBatis-Plus_第22张图片

LambdaUpdateWraper

    /**
     * 更新语句之LambdaUpdateWrapper
     * update table set balance = balance - 200 where id in(1,2,4);
     */
    @Test
    void testLambdaUpdateWrapper() {
        // 1.创建LambdaUpdateWrapper对象
        LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 2.链式编程,构建更新条件,方法参数传对应字段的get方法
        // 使用set方法来设置要更新的字段和新值
        lambdaUpdateWrapper
                .setSql("balance = balance - 200")
                .in(User::getId, List.of(1L, 2L, 4L));
        // 3.执行更新   userMapper(要更新的数据,更新的where条件)
        // 注意:第一个参数可以传null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSql来更新
        userMapper.update(null, lambdaUpdateWrapper);
    }

2.2 自定义SQL

在演示的UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句:

MyBatis-Plus_第23张图片

这种写法在某些企业是不允许的,因为SQL语句最好都维护在持久层,而不是业务层

就当前案例来说,由于条件时in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL,这实在是太麻烦了,假如查询条件更复杂,动态SQL的编写也会更加复杂!

所以MyBatis-Plus提供了自定义SQL功能,让我们可以利用MyBatis-Plus的Wrapper来构建生成复杂的where查询条件,然后再结合Mapper.xml编写自己定义SQL语句中剩下的部分,MP它更擅长的是where条件的编写。 

 

你可能感兴趣的:(mybatis,java,开发语言)