MyBatis-Plus

一、MyBatis-Plus入门

  大家在日常开发中应该能发现,单表的CRUD功能代码重复度很高,也没有什么难度。而这部分代码量往往比较大,开发起来比较费时。因此,目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是MyBatis-Plus。MyBatis-Plus不仅仅可以简化单表操作,而且还对Mybatis的功能有很多的增强。可以让我们的开发更加的简单,高效。MyBatis-Plus和Mybatis是协作关系。

MyBatis-Plus官网地址

在这里插入图片描述

1.1、快速开始

1.引入依赖。MyBatis-Plus提供了starter,实现了自动Mybatis以及MyBatis-Plus的自动装配功能。

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

2.定义Mapper。

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

在这里插入图片描述

因此我们自定义的Mapper只要实现了这个 BaseMapper ,就无需自己实现单表CRUD了。定义一个User类,对应一张数据表。

在这里插入图片描述
让UserMapper接口继承BaseMapper,然后可以编写代码实现单表的CRUD。实际上就是利用MyBatis-Plus省去了以前需要频繁编写的mapper语句。

在这里插入图片描述

1.2、常见注解

  在刚刚的入门案例中,我们仅仅引入了依赖,继承了BaseMapper就能使用MyBatis-Plus,非常简单。但是问题来了,MyBatis-Plus如何知道我们要查询的是哪张表?表中有哪些字段呢?UserMapper在继承BaseMapper的时候指定了一个泛型,泛型中的User就是与数据库对应的PO。MyBatis-Plus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息,从而生成SQL的。默认情况下:

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

但很多情况下,默认的实现与实际场景不符,因此MyBatis-Plus提供了一些注解便于我们声明表信息。

1.2.1、@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类

在这里插入图片描述

1.2.2、@TableId

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

在这里插入图片描述
比较常见的type有三种,对应主键id的生成策略:

  • AUTO:利用数据库的id自增长
  • INPUT:手动生成id
  • ASSIGN_ID:雪花算法生成Long类型的全局唯一id,这是默认的ID策略

1.2.3、@TableField

  • 描述:普通字段注解

在这里插入图片描述

一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:

  • 成员变量名与数据库字段名不一致
  • 成员变量是以isXXX命名,按照JavaBean的规范,MyBatis-Plus识别字段时会把is去除,这就导致与数据库不符。
  • 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:``
  • 成员变量不是数据库字段

 
除了上面介绍的三个常用配置,其他配置可以看官网:

在这里插入图片描述

1.3、常见配置

  MyBatis-Plus 提供了丰富的配置选项,以满足不同用户的需求。这些配置中,一部分继承自 MyBatis 原生支持的配置,另一部分则是 MyBatis-Plus 特有的扩展配置。

在这里插入图片描述

二、核心功能

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

2.1、条件构造器

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

在这里插入图片描述
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:

在这里插入图片描述
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法

在这里插入图片描述
QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许查询指定字段

在这里插入图片描述
UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分

在这里插入图片描述

2.1.1、QueryWrapper

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

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

在这里插入图片描述

@Test
void testQueryWrapper() {
    // 1.构建查询条件 where name like "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .select("id", "username", "info", "balance")
            .like("username", "o")
            .ge("balance", 1000);
    // 2.查询数据
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

2、查询:更新用户名为jack的用户的余额为2000,代码如下:

在这里插入图片描述

@Test
void testUpdateByQueryWrapper() {
    // 1.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 2.更新数据,user中非null字段都会作为set语句
    User user = new User();
    user.setBalance(2000);
    userMapper.update(user, wrapper);
}

2.1.2、UpdateWrapper

  基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)。SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:

@Test
void testUpdateWrapper() {
    List<Long> ids = List.of(1L, 2L, 4L);
    // 1.生成SQL
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200") // SET balance = balance - 200
            .in("id", ids); // WHERE id in (1, 2, 4)
        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
    // 而是基于UpdateWrapper中的setSQL来更新
    userMapper.update(null, wrapper);
}

2.1.3、LambdaQueryWrapper

  无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给Mybatis-Plus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

分别对应QueryWrapper和UpdateWrapper,改造2.1.1中的例子:

@Test
void testLambdaQueryWrapper() {
    // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.lambda()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    // 2.查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

2.2、自定义SQL

  在演示UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句,这种写法在某些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL。这实在是太麻烦了:

在这里插入图片描述
  假如查询条件更复杂,动态SQL的编写也会更加复杂。所以,Mybatis-Plus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL

@Test
void testCustomWrapper() {
    // 1.准备自定义查询条件
    List<Long> ids = List.of(1L, 2L, 4L);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);

    // 2.调用mapper的自定义方法,直接传递Wrapper
    userMapper.deductBalanceByIds(200, wrapper);
}

然后在UserMapper中自定义SQL,在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;

public interface UserMapper extends BaseMapper<User> {
    @Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
    void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}

2.3、Service接口

  Mybatis-Plus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法,这样一些常用的service也不用自己写了。通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
    在这里插入图片描述
  • remove:删除
    在这里插入图片描述
  • update:更新
    在这里插入图片描述
  • get:查询单个结果
    在这里插入图片描述
  • list:查询集合结果
    在这里插入图片描述
  • count:计数
    在这里插入图片描述
  • page:分页查询
    在这里插入图片描述

2.3.1、基本用法

  由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的方法了,如下图所示:

在这里插入图片描述

2.3.2、批量新增性能对比

1.for循环逐条新增:这种方式是最慢的,相当于每新增一条数据都需要网络请求数据库,而网络请求是耗费时间的。

在这里插入图片描述
2.MybatisPlus的默认批量新增:每1000条(因为每次请求数据库的数据包大小是有上限的)网络请求一次数据库新增数据。其实MybatisPlus的批处理是基于PrepareStatement的预编译模式,然后批量提交,最终在数据库执行时还是会有多条insert语句,逐条插入数据,还是比较耗时的。

在这里插入图片描述
3.修改参数后的批量新增:为了更快的执行批量新增,我们希望利用一条SQL语句新增多条数据,即:

在这里插入图片描述
  为了实现这个效果,需要增加配置参数,修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true。再次测试插入10万条数据,可以发现速度有非常明显的提升。这时因为在ClientPreparedStatement的executeBatchInternal中,有判断rewriteBatchedStatements值是否为true并重写SQL的功能(将多条SQL语句重写为一条)。

三、拓展功能

除了上述基础功能外,还提供了很多扩展功能,后续有需要可以看:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值