MyBatisPlus指南

262 阅读13分钟

一、概述

1.MyBatis Plus 是什么?

MyBatis Plus(简称 MP)是一个基于 MyBatis 的增强工具,它简化了 MyBatis 的开发流程,提供了更强大、更便捷的功能,使得开发人员能够更加高效地进行数据库操作和开发。MyBatis Plus 提供了一系列的注解和方法,可以帮助开发人员快速构建和执行 SQL 语句,减少了手写 SQL 的工作量。

2.MyBatis Plus 的特点和优势

  1. 简化开发:MyBatis Plus 提供了一系列的注解和方法,可以帮助开发人员快速构建和执行 SQL 语句,减少了手写 SQL 的工作量,提高了开发效率。
  2. 强大的 CRUD 功能:MyBatis Plus 提供了一套强大的 CRUD 方法,可以直接通过调用方法来完成数据库的增删改查操作,无需手动编写 SQL 语句。
  3. 支持分页查询:MyBatis Plus 内置了分页查询的功能,可以方便地进行分页查询操作,减少了开发人员的工作量。
  4. 支持多表关联查询:MyBatis Plus 提供了一系列的方法和注解,可以方便地进行多表关联查询,简化了复杂查询的编写过程。
  5. 支持代码生成器:MyBatis Plus 提供了一个代码生成器,可以根据数据库表结构自动生成实体类、Mapper 接口和 XML 文件,减少了手动编写代码的工作量。
  6. 支持乐观锁和逻辑删除:MyBatis Plus 内置了乐观锁和逻辑删除的功能,可以方便地实现数据的并发控制和逻辑删除操作。
  7. 支持缓存管理:MyBatis Plus 提供了缓存管理的功能,可以方便地配置和使用缓存,提高了系统的性能和响应速度。

3.MyBatis Plus 的适用场景

  1. 中小型项目:MyBatis Plus 简化了 MyBatis 的开发流程,提供了更强大、更便捷的功能,适用于中小型项目的开发。
  2. 数据库操作频繁的项目:MyBatis Plus 提供了强大的 CRUD 功能和分页查询功能,适用于数据库操作频繁的项目。
  3. 多表关联查询较多的项目:MyBatis Plus 提供了多表关联查询的功能,可以方便地进行多表关联查询,适用于多表关联查询较多的项目。
  4. 需要快速开发的项目:MyBatis Plus 提供了代码生成器的功能,可以根据数据库表结构自动生成代码,适用于需要快速开发的项目。
  5. 需要乐观锁和逻辑删除功能的项目:MyBatis Plus 内置了乐观锁和逻辑删除的功能,适用于需要进行数据并发控制和逻辑删除的项目。 总之,MyBatis Plus 是一个功能强大、易于使用的数据库操作工具,适用于各种类型的项目开发。

二、快速入门

2.1初始化工程

创建一个空的 Spring Boot 工程

图1.png

这里我们直接跳过

图2.png

2.2添加依赖

引入 Spring Boot Starter 父工程:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5+ 版本</version>
    <relativePath/>
</parent>

引入 spring-boot-starterspring-boot-starter-testmybatis-plus-boot-startermysql lombok依赖:

<dependencies>
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
</dependencies>

2.3配置

在application.yml配置文件中添加mysql数据库的相关配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/testdb?serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: 123456

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

@SpringBootApplication
//根据自己的项目路径修改
@MapperScan("com.cqgcxy.mapper")
public class Application {
​
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
​
}

2.4编码

编写实体类 User

@Data
@TableName("`user`")//指定数据库的user表
public class User {
    
    @TableId(value = "uid", type = IdType.AUTO)
    private Integer uid;
​
    @TableField("userName")
    private String username;
​
    @TableField("userPassword")
    private  String userpassword;
​
    private Integer sex;
​
    private String hobbit;
​
    private String degree;
​
    private String note;
​
    private Integer rule;
​
    private Integer state;
​
}

编写 Mapper 包下的 UserMapper接口

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

2.5开始使用

MybatisPlus提供了很多模板供我们使用,我们在这里对部分模板进行学习, 选择测试类,进行功能测试:

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

/**
     * 查询user表全部数据
     */
    @Test
    void selectUserAll() {
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

控制台输出

User(uid=1, username=aa, userpassword=123456, sex=0, hobbit=打游戏, degree=本科, note=张三, rule=0, state=1)
User(uid=2, username=bb, userpassword=123456, sex=1, hobbit=唱, degree=博士, note=李四, rule=0, state=1)
User(uid=3, username=cc, userpassword=123456, sex=0, hobbit=跳, degree=硕士, note=王五, rule=1, state=1)
User(uid=4, username=哈哈, userpassword=123456, sex=1, hobbit=rap, degree=本科, note=老六, rule=0, state=1)
User(uid=13, username=小小, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=14, username=大大, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=15, username=大明, userpassword=123456, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)
User(uid=16, username=小a, userpassword=987654, sex=null, hobbit=null, degree=null, note=null, rule=null, state=null)

三、实体类的常用注解配置

1.@TableName 注解

用于指定实体类对应的数据库表名。可以在实体类上使用该注解,通过value属性指定表名。

2.@TableField 注解

用于指定实体类中字段与数据库表中字段的映射关系。可以在实体类的属性上使用该注解,通过value属性指定字段名。

3.@TableId 注解

用于指定实体类中的主键字段。可以在实体类的属性上使用该注解,通过value属性指定主键字段名。

4.@Version 注解

用于实现乐观锁机制。可以在实体类的属性上使用该注解,表示该字段用于记录数据版本号 。

5.@TableLogic 注解

用于实现逻辑删除功能。可以在实体类的属性上使用该注解,表示该字段用于标记逻辑删除状态。

6.@TableIndex 注解

用于指定数据库表的索引。可以在实体类上使用该注解,通过value属性指定索引名,通过columns属性指定索引的字段。

四、掌握CRUD操作和条件构造器的使用

1.什么是CRUD?基本的 CRUD 的使用

CRUD是指对数据进行增加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)等操作的一种常见的数据操作方式。

  • Create:创建数据,即向数据库中插入新的记录。
  • Retrieve:查询数据,即从数据库中获取符合条件的记录。
  • Update:更新数据,即修改数据库中已有的记录。
  • Delete:删除数据,即从数据库中删除指定的记录

而通过使用MyBatis-Plus的模板相比mybatis,可以简化CRUD操作的编写,提高开发效率,

新增:

    /**
     * 新增
     * @param user
     * @return
     */
    @PostMapping("save")
    public Boolean add(@RequestBody User user){
        return userService.save(user);
    }

删除

    /**
     * 删除(根据id)
     * @param id
     * @return
     */
    @DeleteMapping("delete")
    public Boolean delete(int id){
        return userService.removeById(id);
    }

修改(如果数据存在则为修改,如果数据不存在则为新增)

    /**
     * 新增或修改(根据id)
     * @param user
     * @return
     */
    @PostMapping("saveOrUpdate")
    public Boolean update(@RequestBody User user){
        return userService.saveOrUpdate(user);
    }

查询

    /**
     * 查询
     * @return
     */
    @GetMapping("list")
    public List<User>  list(){
        return userService.list();
    }

2.条件构造器

1.QueryWrapper

使用QueryWrapper构建查询条件,通过eq方法添加年龄等于20的条件,通过like方法添加姓名包含"Tom"的条件,通过orderByDesc方法按照创建时间降序排序,然后使用selectList方法执行查询操作。

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20)
            .like("name", "Tom")
            .orderByDesc("create_time");

List<User> userList = userMapper.selectList(queryWrapper);

2.LambdaQueryWrapper

使用LambdaQueryWrapper构建查询条件,通过eq方法使用Lambda表达式添加年龄等于20的条件,通过like方法使用Lambda表达式添加姓名包含"Tom"的条件,通过orderByDesc方法使用Lambda表达式按照创建时间降序排序,然后使用selectList方法执行查询操作。

LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getAge, 20)
                 .like(User::getName, "Tom")
                 .orderByDesc(User::getCreateTime);

List<User> userList = userMapper.selectList(lambdaQueryWrapper);

3.UpdateWrapper

使用UpdateWrapper构建更新条件,通过set方法设置年龄为25,通过eq方法添加姓名等于"Tom"的条件,然后使用update方法执行更新操作。

UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("age", 25)
             .eq("name", "Tom");

int rows = userMapper.update(null, updateWrapper);

4.LambdaUpdateWrapper

使用LambdaUpdateWrapper构建更新条件,通过set方法使用Lambda表达式设置年龄为25,通过eq方法使用Lambda表达式添加姓名等于"Tom"的条件,然后使用update方法执行更新操作

LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.set(User::getAge, 25)
                   .eq(User::getName, "Tom");

int rows = userMapper.update(null, lambdaUpdateWrapper);

2.分页模糊查询的实现

 /**
     * 分页模糊查询
     * @param query
     * @return
     */
    @PostMapping("listPage")
    public List<User> liseUserPage(@RequestBody PageParam query){
        HashMap param = query.getParam();
        String name= (String)param.get("username");
        
        // 创建分页对象,设置当前页码和每页显示数量
        Page<User> page = new Page();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());
        
        // 创建查询条件,使用Lambda表达式实现模糊查询
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.like(User::getUsername,name);

        // 执行分页查询,返回查询结果
        IPage result = userService.page(page,lambdaQueryWrapper);

        // 返回查询结果列表
        return result.getRecords();
    }

3.自定义 SQL 的执行

这里用分页模糊查询作为例子

controller层

 /**
     * 自定义分页模糊查询
     * @param query
     * @return
     */
    @PostMapping("listPageCC")
    public List<User> liseUserPageCC(@RequestBody PageParam query){
        HashMap param = query.getParam();
        String name= (String)param.get("username");

        // 创建分页对象,设置当前页码和每页显示数量
        Page<User> page = new Page();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());
        // 创建分页对象,设置当前页码和每页显示数量
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.like(User::getUsername,name);
        // 执行自定义分页查询,返回查询结果
        IPage result = userService.pageCC(page,lambdaQueryWrapper);
        // 返回查询结果列表
        return result.getRecords();
    }

service层

小扩展:

Service是一种设计模式,用于将业务逻辑与数据访问逻辑分离。Service层负责处理业务逻辑,包括数据的处理、转换、验证等。它与具体的数据访问方式(如数据库、文件等)无关,使得业务逻辑更加独立、可复用和可测试。

Serviceimpl(Service接口)定义了业务逻辑的方法,而Service实现类则实现了这些方法,具体实现了业务逻辑的处理。

为什么要这么做呢?

  1. 分离关注点:将业务逻辑与数据访问逻辑分离,使得代码更加清晰、可维护和可测试。
  2. 提高复用性:将业务逻辑封装在Service中,可以在不同的场景下复用。
  3. 降低耦合性:Service层与具体的数据访问方式解耦,使得业务逻辑不依赖于具体的数据访问实现。
  4. 提供事务管理:Service层可以管理事务,确保业务操作的一致性和完整性。

service

public interface UserService extends IService<User> {

    //自定义分页模糊查询
    IPage pageCC(IPage<User> page, Wrapper wrapper);
}

serviceimpl

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;


	//自定义分页模糊查询
    @Override
    public IPage pageCC(IPage<User> page, Wrapper wrapper) {
        return userMapper.pageCC(page,wrapper);
    }


}

mapper层

@Mapper
public interface UserMapper extends BaseMapper<User> {

    //自定义分页模糊查询
    IPage pageCC(IPage<User> page,@Param(Constants.WRAPPER) Wrapper wrapper);//爆红不影响
}

sql语句

${ew.customSqlSegment}是MyBatis Plus框架中的一个占位符,用于动态生成SQL语句的条件部分。在MyBatis Plus中,使用Wrapper对象来构建查询条件,ew就是Wrapper对象的实例。

<?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.cqgcxy.mapper.UserMapper">

	<!-- 自定义分页模糊查询-->
    <select id="pageCC" resultType="com.cqgcxy.pojo.User">
        select * from user ${ew.customSqlSegment}
    </select>

</mapper>

五、代码生成器的使用

5.1 导入依赖

<!--        mybatis-plus支持-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
<!--        代码自动生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>spring-boot-starter-swagger</artifactId>
            <version>1.5.1.RELEASE</version>
        </dependency>

5.2 创建代码生成器

在java/com/cqgcxy/common路径下创建CodeGenerator类

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输⼊" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输⼊正确的" + tip + "!");
    }
    /**
     * 操作步骤:
     * 1.修改数据源包括地址密码信息,对应代码标记:⼀、 下同
     * 2.模块配置,可以修改包名
     * 3.修改模板(这步可忽略)
     * @param args
     */
    public static void main(String[] args) {
        // 代码⽣成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("Jocker");
        gc.setOpen(false);
        gc.setSwagger2(true); //实体属性 Swagger2 注解
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(true);// XML columList
        //去掉service接⼝⾸字⺟的I, 如DO为User则叫UserService
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // ⼀、修改数据源
        dsc.setUrl("jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=UTF8&useSSL=false");
                // dsc.setSchemaName("public");
                dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);
        // 包配置
        PackageConfig pc = new PackageConfig();
        //pc.setModuleName(scanner("模块名"));
        // ⼆、模块配置
        pc.setParent("com.cqgcxy")
                .setEntity("pojo")
                .setMapper("mapper")
                .setService("service")
                .setServiceImpl("service.impl")
                .setController("controller");
        mpg.setPackageInfo(pc);
        // ⾃定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        String templatePath = "templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";
        // ⾃定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // ⾃定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // ⾃定义输出⽂件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发⽣变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" +
                        StringPool.DOT_XML;
            }
        });
 /*
 cfg.setFileCreate(new IFileCreate() {
 @Override
 public boolean isCreate(ConfigBuilder configBuilder, FileType fileType,
String filePath) {
 // 判断⾃定义⽂件夹是否需要创建
 checkDir("调⽤默认⽅法创建的⽬录,⾃定义⽬录⽤");
 if (fileType == FileType.MAPPER) {
 // 已经⽣成 mapper ⽂件判断存在,不想重新⽣成返回 false
 return !new File(filePath).exists();
 }
 // 允许⽣成模板⽂件
 return true;
 }
 });
 */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
//        3. ⽣成代码并测试
//        四、实现增删改查
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置⾃定义输出模板
        //指定⾃定义模板路径,注意不要带上.ftl/.vm, 会根据使⽤的模板引擎⾃动识别
        // 三、修改模板
 /*templateConfig.setEntity("templates/entity2.java");
 templateConfig.setService("templates/service2.java");
 templateConfig.setController("templates/controller2.java");
 templateConfig.setMapper("templates/mapper2.java");
 templateConfig.setServiceImpl("templates/serviceimpl2.java");*/
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // strategy.setSuperEntityClass("你⾃⼰的⽗类实体,没有就不⽤设置!");
        //strategy.setSuperEntityClass("BaseEntity");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共⽗类
        //strategy.setSuperControllerClass("BaseController");
        // strategy.setSuperControllerClass("你⾃⼰的⽗类控制器,没有就不⽤设置!");
        // 写于⽗类中的公共字段
        // strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英⽂逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        //strategy.setTablePrefix(pc.getModuleName() + "_");
        // 忽略表前缀tb_,⽐如说tb_user,直接映射成user对象
        // 四、注意是否要去掉表前缀
        //strategy.setTablePrefix("tb_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

5.3 需修改CodeGenerator的部分代码:

建立对应的包名

图4.png

修改数据数据库配置

图5.png

5.4运行CodeGenerator类

输入数据库存在的表名

图6.png

运行成功,代码生成成功

图7.png

注意添加Mapper注解

代码生成器没有添加Mapper注解,或者在启动类添加@MapperScan扫描包二选一

六、MyBatis Plus的优缺点

优点

  1. 简化开发:MyBatis Plus提供了很多便捷的API和工具,可以大大简化数据库操作的开发工作。例如,通过继承BaseMapper接口,可以直接使用常用的增删改查方法,无需手动编写SQL语句。
  2. 提高效率:MyBatis Plus提供了代码生成器,可以根据数据库表结构自动生成实体类、Mapper接口和XML映射文件,减少了手动编写重复代码的工作量,提高了开发效率。
  3. 强大的查询功能:MyBatis Plus提供了丰富的查询方法和条件构造器,可以灵活地进行复杂的查询操作。例如,可以通过Lambda表达式来构建查询条件,使得查询语句更加简洁和易读。
  4. 支持分页查询:MyBatis Plus内置了分页插件,可以方便地进行分页查询操作,减少了手动编写分页逻辑的工作量。
  5. 支持多种数据库:MyBatis Plus支持多种主流的关系型数据库,如MySQL、Oracle、SQL Server等,可以在不同的数据库之间无缝切换。

不足

  1. 灵活性受限:MyBatis Plus提供了很多便捷的功能和API,但有时候可能会受到框架的限制,无法满足一些特定的需求。在某些复杂的场景下,可能需要手动编写SQL语句或者自定义插件来实现。
  2. 过度封装:MyBatis Plus对MyBatis进行了封装和扩展,提供了更高层次的抽象和封装,但有时候可能会导致开发者对底层的SQL执行细节不够了解,难以进行性能优化和调优。