MyBatis-Plus 基础

说明

教程

视频教程:https://www.bilibili.com/video/BV1Xu411A7tL
详细文档:https://b11et3un53m.feishu.cn/wiki/PsyawI04ei2FQykqfcPcmd7Dnsc

MyBatisPlus作用

MyBatisPlus只需要简单的配置,即可快速进行单表的增删改查。还具有代码生成、自动分页、逻辑删除、自动填充等扩展功能。

注意事项

  • MyBatisPlus擅长的是单表的增删改查,如果多表的查询或者sql语句比较复杂,是需要手写sql语句的。
  • 更新操作,默认只更新非null的数据(更新策略可以在配置中修改)。

常见注解

@MapperScan
扫描mapper的注解,配置要扫描的包路径,扫描到的mapper才能够生效。

实体类上的注解
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:

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

@TableName
表名注解,标识实体类对应的表

@TableId
主键注解,标识实体类中的主键字段

两个属性value和type,其中type的常用类型

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

@TableField
普通字段注解

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

  • 成员变量名与数据库字段名不一致
  • 成员变量是以is开头且是布尔值,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
  • 成员变量名与数据库的关键字冲突。使用@TableField注解给字段名添加````转义
  • 变量不是数据表里的字段

示例:

@TableName("tb_user")
public class User {

    //自增长
    @TableId(type = IdType.AUTO)
    private Long id;

    //变量名与数据表字段名不一致
    @TableField("user_name")
    private String name;

    private Integer age;

    //成员变量是以is开头且是布尔值
    @TableField("is_married")
    private Boolean isMarried;

    //成员变量名与数据库的关键字冲突
    @TableField("`order`")
    private String order;

    //变量不是数据表里的字段
    @TableField(exist = false)
    private String info;
}

常见配置

MybatisPlus也支持基于yaml文件的自定义配置

官方文档:https://www.baomidou.com/reference/

示例:

mybatis-plus:
  type-aliases-package: com.itheima.mp.domain.po # 别名扫描包。当mapper.xml中定义实体类类型,就不用定义全路径名了 
  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为雪花算法生成,设为auto就是自增长
      update-strategy: not_null # 默认更新策略:只更新非空字段

大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值或者需要修改的,例如:

  • 实体类的别名扫描包
  • 全局id类型(默认是雪花算法)
mybatis-plus:
  type-aliases-package: com.itheima.mp.domain.po
  global-config:
    db-config:
      id-type: auto # 全局id类型为自增长

条件构造器

Wrapper(条件构造器)

继承关系:

在这里插入图片描述

Wrapper
AbstractWrapper:提供了where中包含的所有条件构造方法(eq、gt、lt、like等)

  • QueryWrapper:拓展了一个select方法,允许指定查询字段
  • UpdateWrapper:拓展了一个set方法,允许指定SQL中的SET部分
  • AbstractLambdaWrapper:避免写死字段名,基于Lambda表达式
    • LambdaUpdateWrapper
    • LambdaQueryWrapper

条件构造器的使用选择:

  • QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
  • UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
  • 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码

QueryWrapper示例:
在这里插入图片描述

    //查询名字里带o并且balance大于1000的
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .select("id", "username", "info", "balance")
            .like("username", "o")
            .ge("balance", 1000);
    List<User> userList = userMapper.selectList(queryWrapper);

构造器常用方法:

  • eq 就是 equal等于
  • ne 就是 not equal不等于
  • gt 就是 greater than大于
  • lt 就是 less than小于
  • ge 就是 greater than or equal 大于等于
  • le 就是 less than or equal 小于等于
  • in 就是 in 包含(数组)
  • isnull 就是 等于null
  • between 就是 在2个条件之间(包括边界值)
  • like就是 模糊查询

UpdateWrapper示例:

UPDATE user 
    SET balance = balance + 200
    WHERE id in (1, 2, 4)
    //更新id符合条件的行中balance加200
    List<Long> ids = new ArrayList<>();
    Collections.addAll(ids, 1L, 2L, 4L);
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>()
            .setSql("balance = balance + 200")
            .in("id", ids);
    userMapper.update(null, updateWrapper);

LambdaQueryWrapper基于Lambda表达式,使用getter方法结合反射技术获取变量名,可以避免硬编码。
示例:

    //查询名字里带o并且balance大于1000的
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    List<User> userList = userMapper.selectList(queryWrapper);

自定义sql

上面的更新示例中,把sql语句的一部分写到业务代码中了,这种写法在一些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。

所以,MybatisPlus让我们可以利用Wrapper生成查询条件,再结合Mapper.xml编写SQL语句剩下的部分。

业务代码基于Wrapper构建where条件

    // 1. 更新条件
    List<Long> ids = new ArrayList<>();
    Collections.addAll(ids, 1L, 2L, 4L);
    int amount = 200;
    // 2. 定义方法
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
            .in(User::getId, ids);
    // 3. 调用自定义sql方法
    userMapper.updateBalanceByIds(wrapper, 200);

在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew,可以写成 "ew" 或者 Constants.WRAPPER


import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;

public interface UserMapper extends BaseMapper<User> {

    void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaUpdateWrapper<User> wrapper, @Param("amount") int amount);
}

在xml文件中编写sql,并使用传入的Wrapper条件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.user.mapper.UserMapper">

    <update id="updateBalanceByIds">
        UPDATE user SET balance = balance + #{amount} ${ew.customSqlSegment}
    </update>

</mapper>

IService接口

在这里插入图片描述

IService接口方法详细说明

IService的 Lambda 方法

Service 中对 LambdaQueryWrapperLambdaUpdateWrapper 的用法进一步做了简化。我们无需自己通过 new 的方式来创建 Wrapper,而是直接调用 lambdaQuery()lambdaUpdate() 方法:

lambdaQuery() 示例:

	@Override
    public List<UserVO> queryUsers(String username, Integer status, Integer minBalance, Integer maxBalance) {
	    // 查询用户
	    List<User> users = lambdaQuery()
	            .like(username != null, User::getUsername, username)
	            .eq(status != null, User::getStatus, status)
	            .ge(minBalance != null, User::getBalance, minBalance)
	            .le(maxBalance != null, User::getBalance, maxBalance)
	            .list();
	    // 处理vo
	    return BeanUtil.copyToList(users, UserVO.class);
	}

lambdaQuery() 方法中除了可以构建条件,还需要在链式编程的最后添加一个 list(),这是在告诉MyBatisPlus 我们的调用结果需要是一个 list 集合。这里不仅可以用 list(),可选的方法有:

  • one():最多1个结果
  • list():返回集合结果
  • count():返回计数结果

MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果。

lambdaUpdate() 示例:

    // 扣减余额 update tb_user set balance = balance - ?
    int remainBalance = user.getBalance() - money;
    lambdaUpdate()
            .set(User::getBalance, remainBalance) // 更新余额
            .set(remainBalance == 0, User::getStatus, 2) // 动态判断,是否更新status
            .eq(User::getId, id)
            .eq(User::getBalance, user.getBalance()) // 乐观锁
            .update();

修改余额的操作为了防止并发问题,加上了乐观锁,判断当前的余额和之前查到的余额是否一致。

视频教程:https://www.bilibili.com/video/BV1S142197x7?p=11

批量新增

    // 准备10万条数据
    List<User> list = new ArrayList<>(1000);
    for (int i = 1; i <= 100000; i++) {
        list.add(buildUser(i));
        // 每1000条批量插入一次
        if (i % 1000 == 0) {
            userService.saveBatch(list);
            list.clear();
        }
    }

要想将多条插入SQL合并为一条,还需要在 jdbc 的 url 后面添加参数 &rewriteBatchedStatements=true

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: MySQL123

https://www.bilibili.com/video/BV1S142197x7/?p=12

扩展功能

代码生成器

推荐使用一款 MybatisPlus 的插件,它可以基于图形化界面完成 MybatisPlus 的代码生成,非常简单。
在这里插入图片描述
在这里插入图片描述
新版idea好像不能在导航栏增加 “other” 选项卡了,生成代码的两个选项迁移到"tools"选项卡了。

使用:
点击Idea顶部菜单中的other,然后选择 Code Generator
在这里插入图片描述
https://www.bilibili.com/video/BV1S142197x7?p=14

Db静态工具

有的时候 Service 之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个Db静态工具类。

    // 利用Db实现复杂条件查询
    List<User> list = Db.lambdaQuery(User.class)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000)
            .list();

逻辑删除

MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

mybatis-plus:  
  global-config:   
  	db-config:    
  	  logic-delete-field: flag # 全局逻辑删除的实体字段名,字段类型可以是boolean、integer      
  	  logic-delete-value: 1 # 逻辑已删除值(默认为 1)      
  	  logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

逻辑删除本身也有自己的问题,比如:

  • 会导致数据库表垃圾数据越来越多,影响查询效率
  • SQL中全都需要对逻辑删除字段做判断,影响查询效率

因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。

枚举处理器

MybatisPlus提供了一个处理枚举的类型转换器,可以帮我们把枚举类型与数据库类型自动转换。

@Getter
public enum UserStatus {
    NORMAL(1, "正常"),
    FREEZE(2, "冻结")
    ;
    @EnumValue  //此注解指定存入数据库的值
    private final int value;
    @JsonValue  //返回类型可以通过此注解指定
    private final String desc;

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

配置:

mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

JSON类型处理器

MySQL 5.7 版本开始支持JSON 对象和JSON 数组两种类型。
JSON 类型比较适合存储一些列不固定、修改较少、相对静态的数据,比如用户画像、商品标签、接口数据等
MySQL JSON数据类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值