MyBatis-Plus

1. MyBatis-Plus 介绍

MyBatis-Plus (简称MP) 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

特性:

  • 润物无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
  • 效率至上:只需简单配置,即可快速进行单表 CRUD 操作,从而节省大量时间。
  • 丰富功能:代码生成、自动分页、逻辑删除、自动填充、拦截器等功能一应俱全。
  • 广泛认可:连续5年获得开源中国年度最佳开源项目殊荣,Github 累计 16K Star。

支持数据库:

PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, OceanBase, H2, DB2... (任何能使用 MyBatis 进行增删改查,并且支持标准 SQL 的数据库应该都在 MyBatis-Plus 的支持范围内)

官网地址:MyBatis-Plus 🚀 为简化开发而生

2. 使用

2.1 准备工作

2.1.1 数据准备

创建用户表,并创建对应的实体类 User

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;

CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
USE mybatis_test;

-- 创建表[用户表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
        `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
        `username` VARCHAR ( 127 ) NOT NULL,
        `password` VARCHAR ( 127 ) NOT NULL,
        `age` TINYINT ( 4 ) NOT NULL,
        `gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',
        `phone` VARCHAR ( 15 ) DEFAULT NULL,
        `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
        `create_time` DATETIME DEFAULT now(),
        `update_time` DATETIME DEFAULT now() ON UPDATE now(),
        PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 

-- 添加用户信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

2.1.2 项目准备

创建 SpringBoot 工程

添加 MyBatis-Plus 和 MySQL 依赖,配置数据库连接信息

Spring Boot 2.x 版本

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

Spring Boot 3.x 版本

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.9</version>
</dependency>
MySQL 驱动
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
配置数据库

application.yml 文件

# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: "111111"
    driver-class-name: com.mysql.cj.jdbc.Driver

或者 application.properties文件

# 驱动类名称 
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接的 url 
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_testcharacterEncoding=utf8&useSSL=false
# 连接数据库的⽤⼾名
spring.datasource.username=root
# 连接数据库的密码
spring.datasource.password=root

2.2 编码

创建实体类 UserInfo

实体类的属性名与表中的字段名一一对应

import lombok.Data;

import java.util.Date;

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

Mapper 接口类

MybatisPlus 提供了一个基础的 BaseMapper 接口,已经实现了单表的 CRUD,我们自定义 Mapper只需要继承这个 BaseMapper,就无需自己实现单表 CRUD 了。

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mybatisplusdemo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {

}

也可以在启动类上添加 @MapperScan,扫描 Mapper 文件夹,二选一即可

2.3 CRUD 单元测试

使用 Generate 生成 test 单元测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;

    // 查询
    @Test
    void testSelect() {
        System.out.println(userInfoMapper.selectById(1));
    }
}

运行结果:

3. MyBatis-Plus 复杂操作

3.1 常见注解

在上面的程序中,MyBatis 是如何知道我们要操作的是哪张表,表里有哪些字段呢?

看以下 Mapper 代码

@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {

}

UserInfoMapper 在继承 BaseMapper 时,指定了一个泛型,这个 UserInfo 就是与数据库表相对应的实体类。

MyBatis - Plus 会根据这个实体类来推断表的信息。

默认情况下:

1. 表名:实体类的驼峰表示法转换成蛇形表示法(下划线分割),作为表名。比如 UserInfo -> user_info

2. 字段:根据实体类的属性名 转换为蛇形表示法作为字段名。比如 deleteFlag -> delete_flag

3. 主键:默认为 id

那如果实体类和数据库不是按照上述规则定义的呢?MyBatis - Plus 也给我们提供了一下注解,让我们标识表的信息。

3.1.1 @TableName

修改实体类名 UserInfo 为 Userinfo,重新执行测试方法

运行结果

从日志可以看到默认查找的表名为 userinfo

我们可以通过 @TableName 来标识实体类对应的表

@Data
@TableName("user_info")
public class Userinfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

再次运行程序,结果正常

3.1.2 @TableField

修改属性名 deleteFlag 为 deleteflag,重新执行测试方法

运行结果:

从日志可以看到,根据属性名转换后的字段名为 deleteflag

我们可以通过 @TableField 来标识对应的字段名

@Data
@TableName("user_info")
public class Userinfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    @TableField("delete_flag")
    private Integer deleteflag;
    private Date createTime;
    private Date updateTime;
}

再次运行程序,结果正常

3.1.3 @Tableld

在 test 中写一个新增操作:

    // 新增
    @Test
    void testInsert() {
        Userinfo userinfo = new Userinfo();
        userinfo.setUsername("zhaoliu");
        userinfo.setPassword("11111");
        userInfoMapper.insert(userinfo);
    }

运行之后:

这是因为它不知道 id 是自增的,需要在 id 上加上注解 @TableId(type = IdType.AUTO)

还需修改计数器:

修改属性名 id 为 userId,重新执行测试方法

运行结果:

通过 @TableId 来指定对应的主键

@Data
@TableName("user_info")
public class Userinfo {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer userId;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    @TableField("delete_flag")
    private Integer deleteflag;
    private Date createTime;
    private Date updateTime;
}

如果属性名和字段名不一致,需要在 @TableId 指明对应的字段名。

属性名和字段一致的情况下,直接加 @TableId 注解就可以。

再次运行程序,结果正常

tip:修改和删除测试

    // 改
    @Test
    void testUpdate() {
        Userinfo userinfo = new Userinfo();
        userinfo.setUserId(5);
        userinfo.setPassword("1234");
        userInfoMapper.insert(userinfo);
    }
    
    // 删除
    @Test
    void testDelete() {
        userInfoMapper.deleteById(5);
    }

3.2 打印日志

为了方便观察,我们可以借助日志,查看 Mybatis - Plus 执行的 SQL 语句,参数和执行结果 Mybatis - Plus 配置日志如下:

mybatis-plus:
  configuration: # 配置打印 MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.3 条件构造器

上面程序里的使用,都是简单的 CRUD,在实际的应用场景中,我们还需要使用更复杂的操作,MyBatis - Plus 也给我们提供了相应的支持。

MyBatis - Plus 提供了一套强大的条件构造器 (Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。

以下是主要的 Wrapper 类及其功能:

  • AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。详细参考官网介绍:条件构造器
  • QueryWrapper:用于构造查询条件,在 AbstractWrapper 的基础上拓展了一个 select 方法,允许指定查询字段。
  • UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。
  • LambdaQueryWrapper:基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。
  • LambdaUpdateWrapper:基于 Lambda 表达式的更新条件构造器,它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。

3.3.1 QueryWrapper

QueryWrapper 并不只用于查询语句,无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件

查询

完成下述 SQL 查询

SELECT id,username,password,age FROM user_info WHERE age = 18 AND username like "%min%"

测试代码

    @Test
    void testQueryWrapper() {
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "username", "password", "age")
                .eq("age", 18)
                .like("username", "min");
        userInfoMapper.selectList(queryWrapper).forEach(System.out::println); //返回值是一个 List,使用 lambda 表达式打印出来
    }

更新

完成下述 SQL 查询

UPDATE user_info SET delete_flag=? WHERE age < 20

测试代码

    @Test
    void testQueryWrapper2() {
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 20);

        Userinfo userinfo = new Userinfo();
        userinfo.setDeleteflag(1);
        userInfoMapper.update(userinfo, queryWrapper);
    }

这样写还需要创建一个类,很麻烦,可以直接使用 UpdateWrapper 来实现

    @Test
    void testUpdateWrapper() {
        UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("delete_flag", 2)
                .lt("age", 20);
        userInfoMapper.update(updateWrapper);
    }

lt : "less than"的缩写,表示小于。

le : "less than or equal to"的缩写,表示小于等于。

ge : "greater than or equal to"的缩写,表示大于等于。

gt : "greater than"的缩写,表示大于。

eq : "equals"的缩写,表示等于。

ne : "not equals"的缩写,表示不等于。


删除

完成下述 SQL 查询

DELETE FROM user_info WHERE age = 18

测试代码 

    @Test
    void testQueryWrapper3() {
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("age", 18);
        userInfoMapper.delete(queryWrapper);
    }

3.3.2 UpdateWrapper

对于更新,可以直接使用 UpdateWrapper,在不创建实体对象的情况下,直接设置更新字段和条件

基础更新:

完成下述 SQL 查询

UPDATE user_info SET delete_flag=0, age=5 WHERE id IN (1,2,3)

测试代码

    @Test
    void testUpdateWrapper2() {
        UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("delete_flag", 0)
                .set("age", 5)
                .in("id", List.of(1, 2, 3));
        userInfoMapper.update(updateWrapper);
    }

基于 SQL 更新:

完成下述 SQL 查询

UPDATE user_info SET age = age+10 WHERE id IN (1,2,3)

测试代码 

    @Test
    void testUpdateWrapper3() {
        UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
        updateWrapper.setSql("age = age + 10")
                .in("id", List.of(1, 2, 3));
        userInfoMapper.update(updateWrapper);
    }

3.3.3 LambdaQueryWrapper

QueryWrapper 和 UpdateWrapper 存在一个问题,就是需要写死字段名,如果字段名发生变更,可能会因为测试不到位酿成事故。

MyBatis - Plus 给我们提供了一种基于 Lambda 表达式的条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名,也提高了代码的可读性和可维护性。

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

分别对应上述的 QueryWrapper 和 UpdateWrapper。

具体使用:

    @Test
    void testLambdaQueryWrapper() {
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(Userinfo::getUserId, Userinfo::getUsername, Userinfo::getPassword)
                .eq(Userinfo::getAge, 18)
                .like(Userinfo::getUsername, "min");
        userInfoMapper.selectList(queryWrapper).forEach(System.out::println);
    }

3.3.4 LambdaUpdateWrapper

    @Test
    void testLambdaWrapper() {
        UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
        // 普通版
//        updateWrapper.setSql("age = age + 10")
//                .in("id", List.of(1, 2, 3));
        // lambda 版
        updateWrapper.lambda()
                .set(Userinfo::getDeleteflag, 0)
                .set(Userinfo::getAge, 6)
                .in(Userinfo::getUserId, List.of(1, 2, 3));
        userInfoMapper.update(updateWrapper);
    }

3.4 自定义 SQL

在实际的开发中,有时候 MyBatis - Plus 提供的操作依然不能满足我们的实际需求,因此 MyBatis - Plus 也提供了自定义 SQL 的功能,我们可以利用 Wrapper 构造查询条件,再结合 Mapper 编写SQL

为了使用这一功能,MyBatis-Plus 版本不能低于 3.0.7

代码示例 1

完成下述 SQL 查询

select id,username,password,age FROM user_info WHERE username = "admin"

Mapper

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.example.mybatisplusdemo.model.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserInfoMapper extends BaseMapper<Userinfo> {

//    @Select("select id,username,password,age FROM user_info WHERE username = #{name}")
    @Select("select id,username,password,age FROM user_info ${ew.customSqlSegment}")
    List<Userinfo> selectByCustom(@Param(Constants.WRAPPER)Wrapper<Userinfo> wrapper);
}

测试代码

    @Test
    void selectByCustom() {
//        userInfoMapper.selectByCustom("admin").forEach(System.out::println);
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", "admin");
        userInfoMapper.selectByCustom(queryWrapper).forEach(System.out::println);
    }

注意事项:

  • 参数命名:在自定义 SQL 时,传递 Wrapper 对象作为参数时,参数名必须为 ew ,或者使用注解 @Param(Constants.WRAPPER) 明确指定参数为 Wrapper 对象。(注意:此处的 Constanta 和 Wrapper 都是 import com.baomidou.mybatisplus.core.* 下面的)
  • 使用 ${ew.customSqlSegment} :在 SQL 语句中,使用 ${ew.customSqlSegment} 来引用 Wrapper 对象生成的 SQL 片段。
  • 不支持基于 entity 的 where 语句:自定义 SQL 时,Wrapper 对象不会基于实体类自动生成 where子句,你需要手动编写完整的 SQL 语句。

代码示例 2

MyBatis-Plus 在 MyBatis 的基础上只做增强,不做改变,所以也支持 XML 的实现方式

示例 1 中功能通过 XML 方式实现:

配置 mapper 路径

mybatis-plus:
  mapper-locations: "classpath*:/mapper/**.xml" # Mapper.xml

定义方法

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.example.mybatisplusdemo.model.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserInfoMapper extends BaseMapper<Userinfo> {
    List<Userinfo> selectByCustom2(@Param(Constants.WRAPPER)Wrapper<Userinfo> wrapper);
}

编写 XML 

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

    <select id="selectByCustom2" resultType="com.example.mybatisplusdemo.model.Userinfo">
        select id,username,password,age FROM user_info ${ew.customSqlSegment}
    </select>
</mapper>

测试

    @Test
    void selectByCustom2() {
//        userInfoMapper.selectByCustom("admin").forEach(System.out::println);
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", "admin");
        userInfoMapper.selectByCustom2(queryWrapper).forEach(System.out::println);
    }

代码示例 3

完成下述 SQL 查询

UPDATE user_info SET age = age+10 WHERE id IN (1,2,3)

Mapper

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.example.mybatisplusdemo.model.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

@Mapper
public interface UserInfoMapper extends BaseMapper<Userinfo> {
    @Update("update user_info set age = age + #{age} ${ew.customSqlSegment}")
    Integer updateByCustom(@Param("age")Integer age, @Param(Constants.WRAPPER) Wrapper wrapper);
}

测试代码

    @Test
    void updateByCustom() {
        QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", List.of(1, 2, 3));
        userInfoMapper.updateByCustom(10, queryWrapper);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值