MyBatis-Plus

课程内容

  • MyBatis-Plus简介

  • CRUD接口

  • 常用注解

  • 条件构造器

  • 扩展功能

学习目标

  • 能够基于MyBatis-Plus进行单表CRUD操作
  • 掌握MyBatis-Plus的注解使用
  • 掌握MyBatis-Plus的条件构造器使用
  • 掌握id生成策略控制

0. 快速体验

MyBatis-Plus是MyBatis框架的一个增强工具,可以简化持久层代码开发。

image-20230222093601580

接下来,先快速体验一下使用MyBatis-Plus开发持久层,代码是多么的简单。

将课程资料中的mp-demo工程导入IDEA中,如下:

image-20230222094029672

注意:配套的数据库和表结构也需要创建出来,执行下面脚本创建:

CREATE DATABASE mybaitsplus_db DEFAULT CHARACTER SET utf8 ;
USE mybaitsplus_db;
CREATE TABLE `user` (
  `id`        bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号' ,
  `name`      varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名' ,
  `password`  varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码' ,
  `age`       int(3) NOT NULL COMMENT '龄年' ,
  `tel`       varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电话' ,
  PRIMARY KEY (`id`)
);

直接运行单元测试,验证是否正常执行:

image-20230222095205089

通过执行单元测试方法,可以确认代码是可以正常执行的。

接下来可以对比一下原始的mybatis开发持久层代码和MyBatis-Plus开发持久层代码:

  • 原始mybatis开发持久层

image-20230222095540206

  • MyBatis-Plus开发持久层

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

可以看到,使用MyBatis-Plus开发持久层非常简单,只需要继承BaseMapper就可以了,这是因为框架已经提供了常用的单表CRUD方法,如下:

image-20230222104958970

只需要继承 BaseMapper,就可以实现单表 CRUD 操作,无须自己再编写SQL。

1. MyBatis-Plus简介

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

官网:MyBatis-Plus

image-20230222105242237

MyBatis-Plus特性:

• 无侵入:只做增强不做改变,不会对现有工程产生影响

• 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作

• 支持 Lambda:编写查询条件无需担心字段写错

• 支持主键自动生成

• 内置分页插件

• ……

开发方式:

• 单独使用 MyBatis-Plus

• 基于 Spring 使用 MyBatis-Plus

• 基于 SpringBoot 使用 MyBatis-Plus(最常用)

maven坐标:

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

通过maven的依赖传递特性,可以看到MyBatis-Plus底层还是基于mybatis进行数据库操作的:

image-20230222105604388

2. CRUD接口

本节来学习MP框架提供的常用CRUD方法,这些方法都是定义在BaseMapper接口中的。

学习MP,主要就是需要学习BaseMapper接口中的方法,开发过程中会经常使用到这些方法来操作数据库。

image-20230223094242145

2.1 开发环境搭建

①:创建maven工程,并配置相关基础信息

image-20230222140624996

②:配置pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>mp-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
​
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
​
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
​
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
​
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
​
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

③:配置数据源(application.yml)

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root

④:编写 SpringBoot 启动类

package com.itheima;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class MybatisPlusApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
​
}

⑤:创建实体类与表结构(类名与表名对应,属性名与字段名对应)

package com.itheima.domain;
​
import lombok.Data;
​
@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
CREATE TABLE `user` (
  `id`        bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号' ,
  `name`      varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名' ,
  `password`  varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码' ,
  `age`       int(3) NOT NULL COMMENT '龄年' ,
  `tel`       varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电话' ,
  PRIMARY KEY (`id`)
)
​

⑥:定义Mapper接口,继承 BaseMapper

package com.itheima.mapper;
​
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
​
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

2.2 新增操作

新增操作可以调用BaseMapper中提供的insert方法:

/**
 * 插入一条记录
 *
 * @param entity 实体对象
 */
int insert(T entity);

单元测试方法:

/**
 * 测试Mapper接口的insert方法,可以插入一条数据
 */
@Test
void testInsert(){
    User user = new User();
    user.setName("黑马程序员");
    user.setPassword("123456");
    user.setAge(12);
    user.setTel("13812345678");
    user.setOnline("1");
    userMapper.insert(user);
}

为了方便观察MP框架执行的数据库操作,可以在application.yml中配置MP的日志,将sql输出到控制台:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启mp日志,将sql输出到控制台

控制台输出:

image-20230222143117442

2.3 删除操作

删除操作可以调用BaseMapper中提供的deleteById和deleteBatchIds方法:

/**
 * 根据 ID 删除
 *
 * @param id 主键ID
 */
int deleteById(Serializable id);
/**
 * 删除(根据ID 批量删除)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

单元测试方法:

/**
 * 测试Mapper接口的delete方法,可以删除数据
 */
@Test
void testDelete(){
    //根据id删除数据
    int i = userMapper.deleteById(1624945956237721601L);
    System.out.println(i);
​
    //根据id批量删除数据
    int batchIds = userMapper.deleteBatchIds(Arrays.asList(1625061142848167937L, 1625061824372146177L));
    System.out.println(batchIds);
}

控制台输出:

image-20230222143538536

2.4 修改操作

修改操作可以调用BaseMapper中提供的updateById方法:

/**
 * 根据 ID 修改
 *
 * @param entity 实体对象
 */
int updateById(@Param(Constants.ENTITY) T entity);

单元测试方法:

/**
 * 测试Mapper接口的update方法,根据id修改数据
 */
@Test
void testUpdate(){
    User user = new User();
    user.setId(2625307405933957121L);
    user.setName("传智教育");
​
    int i = userMapper.updateById(user);
    System.out.println(i);
}

控制台输出:

image-20230222143836755

2.5 查询操作

查询操作可以调用BaseMapper中提供的如下方法:

/**
 * 根据 ID 查询
 *
 * @param id 主键ID
 */
T selectById(Serializable id);
/**
 * 查询(根据ID 批量查询)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
 * 根据 entity 条件,查询全部记录(并翻页)
 *
 * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

注意如果要实现分页功能,需要配置MP框架的分页拦截器:

package com.itheima.config;
​
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class MPConfiguration {
​
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //添加分页拦截器,实现分页查询功能
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
​
}

单元测试方法:

/**
 * 测试Mapper接口的select方法,可以查询数据
 */
@Test
void testSelect(){
    //根据id查询数据
    User user = userMapper.selectById(1625307405933957121L);
    System.out.println(user);
​
    //根据id批量查询数据
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1625307405933957121L, 1625310776384380930L));
    System.out.println(users);
​
    //分页查询条件对象
    Page<User> page = new Page<>(1,5);
    //分页查询
    userMapper.selectPage(page,null);
    System.out.println(page);
​
    //总记录数
    long total = page.getTotal();
    //分页结果数据
    List<User> records = page.getRecords();
}

控制台输出:

image-20230222151259655

注:可以通过debug方式观察程序的运行过程。

2.6 问题思考

  1. MP 是如何知道要操作 user 表的?

  2. 如果将 user 表改名为 t_user 还能正常操作吗?

  3. 如果将 user 表中的 password 字段改名为 pwd,还能正常操作吗?

3. 常用注解

3.1 @TableName注解

通过@TableName注解可以映射实体类和表的对应关系。

  • 名称:@TableName

  • 类型:类注解

  • 位置:模型类上

  • 作用:设置当前类对应与数据库表关系

  • 范例:

@TableName("t_user") //当前实体类对应的表为t_user
public class User {
    private Long id;
}

注:如果类名和表名一致,MP可以自动进行映射,此时 @TableName 注解可以省略

如果类名使用驼峰命名法命名,表名使用对应的下划线分隔命名,MP可以自动进行映射,此时 @TableName 注解可以省略,如下例子:

image-20230222165623314

3.2 @TableField注解

通过@TableField注解可以映射实体类的属性和表字段的对应关系。

  • 名称:@TableField

  • 类型:属性注解

  • 位置:模型类属性上

  • 作用:设置当前属性对应的数据库表中的字段关系

  • 相关属性:

    value:设置数据库表字段名称

    exist:设置属性在数据库表字段中是否存在,默认为true

  • 范例:

public class User {
    @TableField(value="pwd") //当前属性对应的字段为pwd
    private String password;
    
    @TableField(exist = false) //当前属性在表中没有对应的字段
    private String online;
}

如果属性名和字段名一致,MP可以自动进行映射,此时 @TableField 注解可以省略

如果属性名使用驼峰命名法命名,字段名使用对应的下划线分隔命名,MP可以自动进行映射,此时 @TableField 注解可以省略。

3.3 @TableId注解

通过@TableId注解可以映射实体类的属性和表主键字段的对应关系,还可以设置主键的生成策略。

  • 名称:@TableId

  • 类型:属性注解

  • 位置:模型类中用于表示主键的属性上

  • 作用:映射类中属性和表中主键对应关系,设置主键的生成策略

  • 相关属性:

    value:设置数据库主键字段名称,如果属性名和字段名一致,可以省略此属性

    type:设置主键属性的生成策略,值参照IdType枚举值

  • 范例:

public class User {
    @TableId(type = IdType.AUTO) //当前id属性和表的主键字段id对应,并且设置主键生成策略为AUTO
    private Long id;
}

主键生成策略,在MP框架的IdType枚举中定义:

image-20230222173337489

主键生成策略:

  • AUTO(0):使用数据库id自增策略控制id生成

  • NONE(1):不设置id生成策略

  • INPUT(2):用户手工输入id

  • ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)

  • ASSIGN_UUID(4):以UUID生成算法作为id生成策略

雪花算法(SnowFlake):是Twitter公司推出的专门针对分布式ID的解决方案

结构:符号位+时间戳+工作进程位+序列号位,一个64bit的整数,8字节,正好为一个long类型数据

  • 为了简化开发,可以在application.yml中配置全局的主键生成策略:
mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id #全局设置主键生成策略

注:配置了全局的主键生成策略,在实体类中就无须再配置主键生成策略了。

4. 条件构造器

4.1 条件构造器介绍

通过条件构造器(Wrapper),可以控制最终生成的 SQL 语句的条件部分,如下:

  • select from table where order by

  • update table set where

  • delete from table where

通过条件构造器,就可以控制上面SQL语句中位置的SQL片段,在项目开发过程中经常使用到。

BaseMapper中的很多方法,都需要条件构造器作为参数,如下:

image-20230223095350747

条件构造器的顶级父类为Wrapper:

image-20230223095534766

条件构造器的继承关系如下:

image-20230223095645480

使用比较多的条件构造器:

  • QueryWrapper

  • LambdaQueryWrapper

  • UpdateWrapper

  • LambdaUpdateWrapper

4.2 QueryWrapper

通过 QueryWrapper 条件构造器,可以控制最终生成的查询、删除类的SQL语句。

SQL结构:

  • select ___ from table where ___ order by ___

  • delete from table where ___

BaseMapper中的如下方法可以传入QueryWrapper对象:

image-20230223101108881

image-20230223101313101

示例代码:

image-20230223101440296

注:使用QueryWrapper设置条件时,是通过字符串指定字段名,如果字段名有误,在编译阶段无法发现错误,在程序运行阶段会抛出异常,如下:

image-20230223102124190

4.3 LambdaQueryWrapper

LambdaQueryWrapper的作用和QueryWrapper相同,都是控制最终生成的查询、删除类的SQL语句。不同点在于语法层面。QueryWrapper是通过字段名来设置条件,LambdaQueryWrapper是通过Lambda语法来设置条件,可以做到在编译期就能够发现错误。

示例代码:

image-20230223102814081

项目开发中建议使用 LambdaQueryWrapper 来代替 QueryWrapper。

4.4 UpdateWrapper

通过 UpdateWrapper 条件构造器,可以控制最终生成的更新类的SQL语句。

SQL结构:

  • update table set ___ where ___

BaseMapper中的如下方法可以传入UpdateWrapper对象:

image-20230223103337799

示例代码:

image-20230223103548270

使用 BaseMapper 的 update 方法设置 set 条件时,既可以通过第一个参数(实体对象)设置,也可以通过第二个参数(UpdateWrapper)设置。

注:使用UpdateWrapper设置条件时,是通过字符串指定字段名,如果字段名有误,在编译阶段无法发现错误,在程序运行阶段会抛出异常。

4.5 LambdaUpdateWrapper

LambdaUpdateWrapper的作用和UpdateWrapper相同,都是控制最终生成的更新类的SQL语句。不同点在于语法层面。UpdateWrapper是通过字段名来设置条件,LambdaUpdateWrapper是通过Lambda语法来设置条件,可以做到在编译期就能够发现错误。

示例代码:

image-20230223104936838

项目开发中建议使用 LambdaUpdateWrapper 来代替 UpdateWrapper。

扩展:在使用条件构造器进行条件构造时,可以使用链式编程风格,如下:

//条件构造器
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
//链式编程风格构造条件
queryWrapper
        .select("id", "name")
        .gt("age", 20)
        .eq(tel != null, "tel", tel)
        .like("name", "itcast")
        .orderByAsc("id"); 

4.6 问题思考

已经使用了MP,还能够使用 mybatis 进行 SQL 定义吗?

image-20230223110428222

注意:MP只是对 mybatis 框架进行增强,不会改变 mybatis 框架的使用方法。也就是说原来怎么使用 mybatis 的,现在还可以按照原来的方式使用即可,如下:

image-20230223110601558

为了进一步增强 mybatis 的功能,我们还可以在 Mapper 中声明方法时,方法的参数使用MP提供的Wrapper条件构造器对象,例如:

image-20230224095412396

上面Mapper中声明的findByCondition方法,参数为 Wrapper类型,即条件构造器对象。并且通过@Param注解修饰,为其指定了别名为ew,这样就可以在SQL语句中通过 ${ew.customSqlSegment} 来获取到对应的SQL片段,这个SQL片段就是通过当前Wrapper对象解析成的。

注意,customSqlSegment为固定写法,底层会调用Wrapper对象的getCustomSqlSegment方法来获取对应的SQL片段。下面是Wrapper类的部分源码:

image-20230224100106320

在单元测试方法中测试findByCondition方法:

image-20230224100545495

5. 扩展功能

5.1 逻辑删除

删除数据库中的数据,可以通过物理删除,也可以通过逻辑删除。

  • 物理删除 指的是直接将数据从数据库中删除,执行的是delete语句

  • 逻辑删除 指的是修改数据的某个字段,使其表示为已删除状态,执行的是update语句

如下是逻辑删除的效果,在表中增加deleted字段,标识数据是否被删除(例如:1表示删除,0表示未删除)。

image-20230223140453619

注意:对于重要的、后期可能需要恢复的数据,可以考虑使用逻辑删除

由于在项目开发过程中经常会使用到逻辑删除,所以MP框架已经对逻辑删除提供了实现,我们直接使用即可。

具体使用步骤如下:

第一步:在表中添加逻辑删除字段,用于标识数据的删除状态

image-20230223143112694

第二步:在application.yml中配置逻辑删除相关配置项

image-20230223143133911

第三步:在实体类中加入逻辑删除属性(和表中的逻辑删除字段对应),并加入 @TableLogic 注解

image-20230223143152062

加入逻辑删除后,再次调用BaseMapper的删除方法和查询方法,发出的SQL语句已经发生了变化。

删除数据时发出的SQL为update语句,将deleted字段的值改为1,表示当前数据被删除了。

查询数据时发出的SQL语句自动追加上查询条件 deleted=0,表示查询的数据是未删除的数据。

5.2 MP对于Service层的支持

MP 框架除了可以简化持久层代码开发,还为 Service 层提供了业务接口和实现类,可以简化 Service 层的开发。

MP提供的业务接口:

image-20230223143848664

MP提供的业务层实现类:

image-20230223143942033

使用步骤:

① 业务接口继承 MP 提供的 IService 接口

/**
 * 用户模块的Service接口
 *
 * @Author itcast
 * @Create 2023/2/21
 **/
public interface UserService extends IService<User> {
}

② 业务实现类继承 MP 提供的 ServiceImpl 类

/**
 * 用户模块的业务实现
 *
 * @Author itcast
 * @Create 2023/2/21
 **/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

对于单表操作,可以直接调用MP提供的Service中的方法即可。如果MP的Service中提供的方法不能满足要求,可以自己扩展方法,例如:

  • 在业务接口中声明方法:

image-20230223144554532

  • 在业务实现类中实现方法:

image-20230223144815677

可以看到,在业务实现类中可以调用 ServiceImpl 中的 getBaseMapper 方法获得对应的mapper对象,下面是ServiceImpl的源码:

image-20230223145140196

可以看到,在ServiceImpl类中通过 @AutoWired 注解已经将Mapper对象注入,所以在子类中调用getBaseMapper方法就可以获取到Spring容器中的Mapper对象了。

5.3 代码生成器

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装,如下所示:

image-20230223145652006

代码生成器的操作步骤:

① 在IDEA的DataBase窗口中配置数据源

image-20230223145908019

② 通过代码生成器提供的菜单项生成代码

image-20230223145933168

选中表,然后点右键,在弹出的菜单中点击 MybatisX-Generator,此时会弹出如下窗口:

image-20230223150146886

  • module path:指定代码生成到哪个模块中
  • base-package:指定代码生成的包结构
  • ignore table prefix:忽略表名前缀,例如表名为t_user,此时指定当前输入框为t_,则生成的实体类名就是User,否则生成的实体类名为TUser
  • ignore field prefix:忽略字段名前缀,例如字段名为f_name,此时指定当前输入框为f_,则生成的属性名就是name,否则生成的属性名为fName

点击Next进入下个窗口:

image-20230223150723899

点击Finish,此时就可以生成代码:

image-20230223150856017

通过代码生成器,就可以根据表结构,反向生成对应的实体类、Mapper、Service等,可以简化开发,提供开发效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值