SpringBoot整合Mybatis-Plus

277 阅读7分钟

一 Mybatis-Plus概述

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

二 SpringBoot融合Mybatis-Plus

1. 在pom.xml中引入对应的依赖

image.png

<!--       MybatisPlus场景启动器-->
      <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>3.5.2</version>
      </dependency>

当然,因为是和数据库打交道,所有那些mysql驱动也需要引入,这个在Mybatis里面讲解了,不重复了

2. 添加配置文件

image.png

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
                                                  
#输出SQL日志
logging:
  level:
    root: info
    com.cctv.mapper: debug
3. 编写Mapper接口继承BaseMapper

image.png

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cctv.model.Employee;

public interface EmployeeMapper extends BaseMapper<Employee> {
}

因为BaseMapper中封装了很多可以直接使用的函数,可以满足大部分日常的CRUD功能

4. 在启动类里面添加@MapperScan注解

image.png

@MapperScan注解里面的值是自己project里面的Mapper包

三 Mybatis-Plus中的基本CRUD

我们这里使用mapper来做CRUD,所以可以通过@Autowired注入之前在mapper包自定义的mapper接口

1. 增(insert)

image.png

2. 删(delete)

image.png

3. 改(update)

image.png

4. 查(select)

image.png

这里重点讲解一下那些By的. ById就是根据id来操作,我们传入id即可.ByMap是根据map来操作,map里面的key必须是数据库里面的字段名,然后根据k/v来进行操作.selectBatchIds是传入一个数组,数组里面存放的是id值,底层调用了数据库的IN语法

四 Mybatis-Plus注解

1. @TableName: 用于定义表名

image.png

2. TableId: 用于定义表的主键

image.png

value: 数据库主键字段名

type: 用于定义主键类型(主键策略idType)

主键策略介绍
idType.AUTO主键自增
idType.NONE未设置主键
idType.INPUT主要自己输入主键
idType.ASSIGN_ID系统分配ID,用于数据类型数据(long,对应mysql中的BIGINT类型)
idType.ASSING_UUID系统分配UUID,用于字符串型类型,(String,对应mysql中的varchar(32))
3. @TableField: 用于定义非主键字段

value: 用于定义非主键数据库字段名

exist: 用于指定是否为数据库字段

fill: 用于字段填充策略

3.1 字段填充策略

添加,修改数据时,每次都会使用相同的方式进行填充,比如说数据的创建时间,修改时间等,Mybatis-Plus支持自动填充策略

(1) 使用@TableField注解,标注需要进行填充的字段

image.png

(2) 自定义一个类,实现MetaObjectHandler接口,并重写方法,添加@Component注解

image.png

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createDate",new Date(),metaObject);
        this.setFieldValByName("modifyDate",new Date(),metaObject);

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("modifyDate",new Date(),metaObject);
    }
}
4. @TableLogic

@TableLogic: 逻辑删除. 逻辑删除相对于物理删除是修改数据的某个字段的状态,字段仍然保留在数据库中,只是在查询的时候不显示而已.我们可以给数据库增加一个delete_flag字段,用于表示逻辑删除

image.png

image.png

value: 表示没有删除的时候的状态,delval: 表示删除的状态.我们在使用mapper接口的那些方法时候底层会默认给我们加上这些查询条件

5. @Version

在多用户环境中,在同一时间可能会有多个用户更新相同的数据,这会产生冲突,这就是著名的并发性问题,解决的方案之一就是加乐观锁.

举个例子:

未加乐观锁:

张三想修改price-->update t1 set price=100;然后李四又想修改,update t1 set price=150;这时李四就会覆盖掉张三修改的信息,从而产生冲突

加了乐观锁:

张三修改update t1 set price=100,version=2 where version=1;然后李四再来修改的时候就会是update t1 set price=150 where version=1;由于之前version改变了,导致李四修改会出现失败,从而解决了并发问题.

5.1 在对应属性添加@Version注解

image.png

5.2 编写一个配置类将OptimisticLockerInterceptor添加到拦截器

image.png

五 Mybatis-Plus中使用Service

1. 自定义一个service接口继承IService

image.png

2. 自定义service实现类继承ServiceImpl同时实现"1"中自定义的service

image.png

3. Wrapper

IService里面封装BaseMapper里面的很多CRUD方法,我们可以直接调用.我这里将一个重点Wrapper(筛选条件)

image.png

QueryWrapper(LambdaQueryWrapper)是用于查询;UpdateWrapper(LambdaUpdateWrapper)适用于修改.

3.1 QueryWrapper

image.png

无论是select还是eq里面的第一个都是数据库里面的字段名,第二个就是需要操作的值

3.2 LambdaQueryWrapper

image.png

相比较于QueryWrapper,LambdaQueryWrapper里面传递的就是把数据库里面的字段名改成"实体类::getXXX"

3.3 UpdateWrapper

image.png

相比较QueryWrapper,这里使用set,因为要涉及到修改.同时里面传递的参数是数据库字段名

3.4 LambdaUpdateWrapper

image.png

相比于UpdateWrapper,这里所有的方法传递的参数从数据库字段名改成了"实体类::getXXX"

3.5 Wrapper常用方法

eq: 等于(=)

ne: 不等于(!=)

gt: 大于(>)

ge: 大于等于(>=)

lt: 小于(<)

le: 小于等于(<=)

select: 查询

set: 修改

like: 模糊查询

六 Mybatis-Plus分页插件

Mybatis-Plus内部整合了PageHelper,所以我们只需要引入即可

1. 在拦截器引入PageHelper

image.png

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();


        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}
2. 引入hutool工具(为了序列化)

hutool官网: hutool.cn/

image.png

<dependency>
   <groupId>cn.hutool</groupId>
   <artifactId>hutool-all</artifactId>
   <version>5.8.16</version>
</dependency>
3. 使用PageHelper

image.png

current: 代表当前页, size: 代表每页数量

可以直接使用service调用page来使用分页

JSONUtil.toJsonStr: 用于序列化

七 Mybatis-Plus自定义sql

1. 在resource下面新建一个mapper包,然后在mapper下新建对应的xml文件

image.png

2. 在之前自定义的mapper接口中自定义方法,然后在对应的xml文件中写对应的sql语句,类似于之前Mybatis

八 Mybatis-Plus代码生成器(类似于Mybatis中的逆向工程)

1. 导入依赖

image.png

<!-- 代码生成器的依赖 -->
<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-generator</artifactId>
   <version>3.4.1</version>
</dependency>

<!--velocity模板引擎-->
<dependency>
   <groupId>org.apache.velocity</groupId>
   <artifactId>velocity-engine-core</artifactId>
   <version>2.2</version>
</dependency>
2. 在工程下生成一个类,并且配置

image.png

package com.cctv;

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.LikeTable;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class GeneratorApp {

    /**
     * <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 + "!");
    }

    public static void main(String[] args) {

        String moduleName = scanner("模块名");
        String tableName = scanner("表名(多个用,号分隔,或者按前缀(pms*))");
        String prefixName = scanner("需要替换的表前缀");


        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 获得当前项目的路径
        String projectPath = System.getProperty("user.dir")+"/generator";
        // 设置生成路径
        gc.setOutputDir(projectPath + "/src/main/java");
        // 作者
        gc.setAuthor("沐珀尘");
        // 代码生成是不是要打开所在文件夹
        gc.setOpen(false);
        // 生成Swagger2注解
        gc.setSwagger2(true);
        // 会在mapper.xml 生成一个基础的<ResultMap> 映射所有的字段
        gc.setBaseResultMap(true);
        // 同文件生成覆盖
        gc.setFileOverride(true);
        //gc.setDateType(DateType.ONLY_DATE)
        // 实体名:直接用表名  %s=表名
        gc.setEntityName("%s");
        // mapper接口名
        gc.setMapperName("%sMapper");
        // mapper.xml 文件名
        gc.setXmlName("%sMapper");
        // 业务逻辑类接口名
        gc.setServiceName("%sService");
        // 业务逻辑类实现类名
        gc.setServiceImplName("%sImplService");
        // 将全局配置设置到AutoGenerator
        mpg.setGlobalConfig(gc);



        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //  模块名
        pc.setModuleName(moduleName);
        // 包名
        pc.setParent("com.cctv");
        // 完整的报名: com.cctv.pms
        mpg.setPackageInfo(pc);


        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 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.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 把已有的xml生成置空
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // 表名的生成策略:下划线转驼峰 pms_product -- PmsProduct
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 列名的生成策略:下划线转驼峰 last_name -- lastName
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);
        // 在controller类上是否生成@RestController
        strategy.setRestControllerStyle(true);
        // 公共父类
        //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");

        if(tableName.indexOf('*')>0){
            // 按前缀生成表
            strategy.setLikeTable(new LikeTable(tableName.replace('*','_')));
        }
        else{
            // 要生成的表名 多个用逗号分隔
            strategy.setInclude(tableName);
        }
        // 设置表替换前缀
        strategy.setTablePrefix(prefixName);
        // 驼峰转连字符 比如 pms_product --> controller @RequestMapping("/pms/pmsProduct")
        //strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);

        // 进行生成
        mpg.execute();

    }
}

image.png

2.1 模块名

image.png

2.2 表名

自然就是数据库名表,如果有多个可以用","隔开,如果前缀一样可以使用图中样例

2.3 替换的前缀

因为数据库表名大部分采用蛇形命名,但是我们生成javaBean实体类采用的是小驼峰,所以需要把部分东西替换掉