Mybatis-Plus(企业实际开发应用)

一、Mybatis-Plus简介

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

官网:MyBatis-Plus

当前版本:

XML 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>3.5.3.2</version>
</dependency>

MyBatis-Plus特性:

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

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

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

• 支持主键自动生成

• 内置分页插件

开发方式:

• 单独使用 MyBatis-Plus

• 基于 Spring 使用 MyBatis-Plus

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

二、CRUD接口

2.1环境搭建

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

②:配置pom文件

XML 复制代码
<?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)

bash 复制代码
#数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp?useSSL=false&serverTimezone=UTC
    username: root
    password: root
#mybatis-plus
mybatis-plus:
  mapper-locations: classpath:/mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

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

可以使用MybatisHelper插件自动生成实体类和Mapper

java 复制代码
@TableName(value = "`user`")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    /**
     * 用户id
     */
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;

    /**
     * 用户名
     */
    @TableField(value = "username")
    private String username;

    /**
     * 密码
     */
    @TableField(value = "`password`")
    private String password;

    /**
     * 注册手机号
     */
    @TableField(value = "phone")
    private String phone;

    /**
     * 详细信息
     */
    @TableField(value = "info")
    private String info;

    /**
     * 使用状态(1正常 2冻结)
     */
    @TableField(value = "`status`")
    private Integer status;

    /**
     * 账户余额
     */
    @TableField(value = "balance")
    private Integer balance;

    /**
     * 创建时间
     */
    @TableField(value = "create_time")
    private Date createTime;

    /**
     * 更新时间
     */
    @TableField(value = "update_time")
    private Date updateTime;

    /**
     * 获取用户id
     *
     * @return id - 用户id
     */
    public Long getId() {
        return id;
    }

    /**
     * 设置用户id
     *
     * @param id 用户id
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * 获取用户名
     *
     * @return username - 用户名
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置用户名
     *
     * @param username 用户名
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取密码
     *
     * @return password - 密码
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置密码
     *
     * @param password 密码
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 获取注册手机号
     *
     * @return phone - 注册手机号
     */
    public String getPhone() {
        return phone;
    }

    /**
     * 设置注册手机号
     *
     * @param phone 注册手机号
     */
    public void setPhone(String phone) {
        this.phone = phone;
    }

    /**
     * 获取详细信息
     *
     * @return info - 详细信息
     */
    public String getInfo() {
        return info;
    }

    /**
     * 设置详细信息
     *
     * @param info 详细信息
     */
    public void setInfo(String info) {
        this.info = info;
    }

    /**
     * 获取使用状态(1正常 2冻结)
     *
     * @return status - 使用状态(1正常 2冻结)
     */
    public Integer getStatus() {
        return status;
    }

    /**
     * 设置使用状态(1正常 2冻结)
     *
     * @param status 使用状态(1正常 2冻结)
     */
    public void setStatus(Integer status) {
        this.status = status;
    }

    /**
     * 获取账户余额
     *
     * @return balance - 账户余额
     */
    public Integer getBalance() {
        return balance;
    }

    /**
     * 设置账户余额
     *
     * @param balance 账户余额
     */
    public void setBalance(Integer balance) {
        this.balance = balance;
    }

    /**
     * 获取创建时间
     *
     * @return create_time - 创建时间
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * 设置创建时间
     *
     * @param createTime 创建时间
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    /**
     * 获取更新时间
     *
     * @return update_time - 更新时间
     */
    public Date getUpdateTime() {
        return updateTime;
    }

    /**
     * 设置更新时间
     *
     * @param updateTime 更新时间
     */
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

Mapper

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.2 新增操作

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

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

单元测试方法:

java 复制代码
    /**
     * 新增数据
     */
@Test
    public void testInsert(){
        User user = User.builder()
                .username("老八1")
                .password(Base64.getEncoder().encodeToString("123456".getBytes()))
                .phone("18941199302")
                .info("{\"age\": 20, \"intro\": \"青涩老八\", \"gender\": \"female\"}")
                .createTime(Date.from(Instant.now()))
                .updateTime(Date.from(Instant.now()))
                .build();
        userMapper.insert(user);

    }
2.3 删除操作

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

java 复制代码
/**
 * 根据 ID 删除
 *
 * @param id 主键ID
 */
int deleteById(Serializable id);

单元测试方法:

java 复制代码
    /**
     * 删除数据
     */
    @Test
    public void testDelete(){
        userMapper.deleteById(7L);
    }
java 复制代码
/**
 * 删除(根据ID 批量删除)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

单元测试方法:

java 复制代码
//根据id批量删除数据
    int batchIds = userMapper.deleteBatchIds(1L,2L,3L);//可变参数
    System.out.println(batchIds);
2.4 修改操作
java 复制代码
/**
 * 根据 ID 修改
 *
 * @param entity 实体对象
 */
int updateById(@Param(Constants.ENTITY) T entity);

单元测试方法:

java 复制代码
    /**
     * 根据Id修改
     */
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(2L);
        user.setUsername("老六");
        userMapper.updateById(user);
    }
2.5 查询操作
java 复制代码
/**
 * 根据 ID 查询
 *
 * @param id 主键ID
 */
T selectById(Serializable id);

单元测试:

java 复制代码
    /**
     * 根据Id查找
     */
    @Test
    public void testSelect(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
java 复制代码
/**
 * 查询(根据ID 批量查询)
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

单元测试:

java 复制代码
   //根据id批量查询数据
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1625307405933957121L, 1625310776384380930L));
    System.out.println(users);
java 复制代码
/**
 * 根据 entity 条件,查询全部记录(并翻页)
 *
 * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 */
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

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

java 复制代码
@Configuration
public class MPConfig {

    @Bean
    public MybatisPlusInterceptor  pageinitInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分页配置
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL) );
        return mybatisPlusInterceptor;
    }
}

单元测试:

java 复制代码
    /**
     * 分页查询
     */
    @Test
    public void findPage(){
        IPage<User> page = new Page<>(1,3);
        userMapper.selectPage(page,null);
        System.out.println("总页数:" + page.getPages());
        System.out.println("总记录数:" + page.getTotal());
        System.out.println("当前页的页码:" + page.getCurrent());
        System.out.println("每页的数量:" + page.getSize());
        System.out.println("查到的当前页的内容:" + page.getRecords());

    }

三、常用注解

3.1 @TableName注解

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

  • 名称:@TableName

  • 类型:类注解

  • 位置:模型类上

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

  • 范例:

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

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

3.2 @TableField注解

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

  • 名称:@TableField

  • 类型:属性注解

  • 位置:模型类属性上

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

  • 相关属性:

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

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

  • 范例:

java 复制代码
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枚举值

  • 范例:

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

主键生成策略:

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

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

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

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

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

为了简化开发,可以在application.yml中配置全局的主键生成策略:

bash 复制代码
mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id #全局设置主键生成策略

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

四、条件构造器

4.1 条件构造器介绍

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

  • select_____________ from table where_________ order by

  • update table set________ where_________

  • delete from table where___________

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

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

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

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

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

  • QueryWrapper

  • LambdaQueryWrapper

  • UpdateWrapper

  • LambdaUpdateWrapper

4.2 QueryWrapper

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

SQL结构:

  • select __ _ from table where __ _ order by ___

  • delete from table where ___

java 复制代码
  /**
     * 根据条件动态查询
     */
    @Test
    public void selectList(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","username","password","info") //设置查询字段
                .like("username","浩") //模糊查询
                .orderByDesc("id"); //排序条件
        List<User> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);

    }

分页查询

java 复制代码
    /**
     * 分页查询
     */
    @Test
    public void findPage() {
        IPage<User> page = new Page<>(1, 3);
        userMapper.selectPage(page, null);
        System.out.println("总页数:" + page.getPages());
        System.out.println("总记录数:" + page.getTotal());
        System.out.println("当前页的页码:" + page.getCurrent());
        System.out.println("每页的数量:" + page.getSize());
        System.out.println("查到的当前页的内容:" + page.getRecords());

    }

配置分页拦截器

java 复制代码
/**
 * TODO 类描述
 *
 * @author Aaron.
 * @date 2023/10/24 11:18
 */
@Configuration
public class MPConfig {

    @Bean
    public MybatisPlusInterceptor  pageinitInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分页配置
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL) );
        return mybatisPlusInterceptor;
    }
}
4.3 LambdaQueryWrapper

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

java 复制代码
    @Test
    public void testLambdaQueryByQueryWrapper() {

        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>()
                .like(User::getUsername, "o")
                .gt(User::getBalance, 2000);
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        for (User user : users) {
            System.out.println(user);
        }
    }

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

4.4 UpdateWrapper

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

SQL结构:

  • update table set __ _ where ___

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

java 复制代码
    //将Id为1、5、7的人员工资扣200
    @Test
    public void testUpdateWrapper() {
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")
                .in("id", 1L, 5L, 7L);

        userMapper.update(null, wrapper);
    }

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

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

4.5 LambdaUpdateWrapper

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

java 复制代码
    //使用LambdaWrapper进行查询
    @Test
    public void testSelectWithLambdaWrapper() {
        //select username,info from user where status = 1 order by balance Desc
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getUsername, User::getInfo)
                .eq(User::getStatus, 1)
                .orderByDesc(User::getBalance);
        userMapper.selectList(wrapper);

    }
4.6 润物无声

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

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

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

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

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

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

五、扩展功能

5.1 逻辑删除

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

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

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

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

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

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

具体使用步骤如下:

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

XML 复制代码
      logic-delete-field: deleted #指定用于标记数据被删除的字段名
      logic-delete-value: 1 #表示已删除
      logic-not-delete-value: 0 #表示未删除

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

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

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

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

5.2 MP对于Service层的支持

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

MP提供的业务接口:

MP提供的业务层实现类:

5.3 代码生成器

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

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

代码生成器的操作步骤:

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

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

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

module path:指定代码生成到哪个模块中

base-package:指定代码生成的包结构

ignore table prefix:忽略表名前缀,例如表名为t_user,此时指定当前输入框为t_,则生成的实体类名就是User,否则生成的实体类名为TUser

ignore field prefix:忽略字段名前缀,例如字段名为f_name,此时指定当前输入框为f_,则生成的属性名就是name,否则生成的属性名为fName

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

相关推荐
风景的人生13 小时前
mybatis映射时候的注意点
java·mybatis
玄〤14 小时前
MyBatis-Plus 核心功能详解:条件构造器、Service 封装与批量优化实践(黑马springcloud微服务课程)(day2)
spring cloud·微服务·mybatis
loading小马19 小时前
Mybatis-Plus超级实用的多种功能用法
java·spring boot·后端·maven·mybatis
高山上有一只小老虎1 天前
mybatisplus分页查询版本 3.5.8 以下和版本 3.5.9及以上的区别
java·spring boot·mybatis
人道领域1 天前
javaWeb从入门到进阶(MyBatis拓展)
java·tomcat·mybatis
J2虾虾1 天前
SpringBoot和mybatis Plus不兼容报错的问题
java·spring boot·mybatis
pp起床2 天前
【苍穹外卖】Day03 菜品管理
java·数据库·mybatis
九皇叔叔2 天前
【01】SpringBoot3 MybatisPlus 工程创建
java·mybatis·springboot3·mybatis plus
BD_Marathon2 天前
MyBatis逆向工程之清晰简洁版
mybatis
九皇叔叔2 天前
【02】SpringBoot3 MybatisPlus 加入日志功能
java·mysql·mybatis·日志·mybatisplus