mybatis-plus

mybatis-plus 介绍

1.简介

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

2.特性

无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

3.支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb

达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

官网地址:https://baomidou.com/

mybatis-plus ->springboot环境搭建

1.引jar包

 <!-- hutool-all 工具类包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.1</version>
        </dependency>
        <!--mybatis-plus 的依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--Velocity模板生成引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>

2.编辑application.yml配置文件

#数据库连接池
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    password: 123456
    username: root
    url: jdbc:mysql:///test2?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
mybatis-plus:
  global-config:
    db-config:
      #id的类型
      id-type: auto
      #逻辑删除的属性
      logic-delete-field: isDelete
      #逻辑未删除的值
      logic-not-delete-value: 0
      #逻辑删除的值
      logic-delete-value: 1
      #枚举类扫描包
  type-enums-package: com.zhuoyue.**
  configuration:
    #是否开启驼峰命名
    map-underscore-to-camel-case: true
    #MyBatis 自动映射策略 NONE:不启用自动映射 PARTIAL:只对非嵌套的 resultMap 进行自动映射 FULL:对所有的 resultMap 都进行自动映射
    auto-mapping-behavior: partial
    #输入日志打印SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.在resources文件夹下增加generator.properties配置数据库信息,mybatis-puls自动生成工具类要用

dataSource.url=jdbc:mysql:///test2?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
dataSource.driverName=com.mysql.jdbc.Driver
dataSource.username=root
dataSource.password=123456
package.base=com.zhuoyue

4.创建自动生成工具类MyBatisPlusGenerator

import cn.hutool.setting.dialect.Props;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
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.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;

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

/**
 * @author o_wanglisheng
 * @description MyBatisPlus代码生成器
 * @created 2022/1/20 17:10
 */
public class MyBatisPlusGenerator {

    public static void main(String[] args) {
         String projectPath = System.getProperty("user.dir") + "";
         //模块名
         String moduleName = "";
         //表名,多个英文逗号分割;当表名中带*号时可以启用通配符模式
         String[] tableNames = new String[]{
                 "wc_fi_account_payment",
                 "wc_fi_account_payment_detail",
                 "wc_fi_account_receipt",
                 "wc_fi_account_receipt_detail"
         };
         // 代码生成器
         AutoGenerator autoGenerator = new AutoGenerator();
         autoGenerator.setGlobalConfig(initGlobalConfig(projectPath));
         autoGenerator.setDataSource(initDataSourceConfig());
         autoGenerator.setPackageInfo(initPackageConfig(moduleName));
         autoGenerator.setCfg(initInjectionConfig(projectPath, moduleName));
         autoGenerator.setTemplate(initTemplateConfig());
         autoGenerator.setStrategy(initStrategyConfig(tableNames));
         autoGenerator.setTemplateEngine(new VelocityTemplateEngine());
         autoGenerator.execute();
    }

    /**
     * 初始化全局配置
     */
    private static GlobalConfig initGlobalConfig(String projectPath) {
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(projectPath + "/src/main/java");
        //创建人
        globalConfig.setAuthor("myl");
        globalConfig.setOpen(false);
        globalConfig.setSwagger2(true);
        globalConfig.setBaseResultMap(true);
        globalConfig.setFileOverride(true);
        globalConfig.setDateType(DateType.ONLY_DATE);
        globalConfig.setEntityName("%s");
        globalConfig.setMapperName("%sMapper");
        globalConfig.setXmlName("%sMapper");
        globalConfig.setServiceName("%sService");
        globalConfig.setServiceImplName("%sServiceImpl");
        globalConfig.setControllerName("%sController");
        return globalConfig;
    }

    /**
     * 初始化数据源配置
     */
    private static DataSourceConfig initDataSourceConfig() {
        Props props = new Props("generator.properties");
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl(props.getStr("dataSource.url"));
        dataSourceConfig.setDriverName(props.getStr("dataSource.driverName"));
        dataSourceConfig.setUsername(props.getStr("dataSource.username"));
        dataSourceConfig.setPassword(props.getStr("dataSource.password"));
        return dataSourceConfig;
    }

    /**
     * 初始化包配置
     */
    private static PackageConfig initPackageConfig(String moduleName) {
        Props props = new Props("generator.properties");
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setModuleName(moduleName);
        packageConfig.setParent(props.getStr("package.base"));
        packageConfig.setEntity("model");
        return packageConfig;
    }

    /**
     * 初始化模板配置
     */
    private static TemplateConfig initTemplateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();
        //可以对controller、service、entity模板进行配置
        //mapper.xml模板需单独配置
        templateConfig.setXml(null);
        return templateConfig;
    }

    /**
     * 初始化策略配置
     */
    private static StrategyConfig initStrategyConfig(String[] tableNames) {
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setEntityLombokModel(true);
        strategyConfig.setRestControllerStyle(true);
        //当表名中带*号时可以启用通配符模式
        if (tableNames.length == 1 && tableNames[0].contains("*")) {
            String[] likeStr = tableNames[0].split("_");
            String likePrefix = likeStr[0] + "_";
            strategyConfig.setLikeTable(new LikeTable(likePrefix));
        } else {
            strategyConfig.setInclude(tableNames);
        }
        return strategyConfig;
    }

    /**
     * 初始化自定义配置
     */
    private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) {
        // 自定义配置
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                // 可用于自定义属性
            }
        };
        // 模板引擎是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/" + moduleName
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        injectionConfig.setFileOutConfigList(focList);
        return injectionConfig;
    }

}

5.书写IsDeleteEnum

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;

/**
 * @author wjj
 */
@Getter
public enum IsDeleteEnum {
    /**
     * 1.已删除
     * 0,未删除
     */
    YES(1,"已删除"),
    NO(0,"未删除");
    @EnumValue
    private final Integer value;
    @JsonValue
    private final String name;

    IsDeleteEnum(Integer value, String name) {
        this.value = value;
        this.name = name;
    }
}

6.在启动类上添加@MapperScan注解

7.配置分页插件MybatisPlusPageConfig

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MybatisPlusPageConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题
     *
     * mp 与 pagehelper 存在依赖 jsqlparser 冲突,不建议混用
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 攻击 SQL 阻断解析器,防止全表更新与删除
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}

8.配置自动填充策略

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * mybatis全局通用属性填充
 *
 * @author wjj
 */
@Slf4j
@Component
public class MybatisMetaHandler implements MetaObjectHandler {

    /**
     * CREATE_USER_ID   创建人
     * CREATE_TIME      创建时间
     * UPDATE_USER_ID   修改人
     * UPDATE_TIME      修改时间
     * IS_DELETE        是否删除(0:否,1是)
     */
    private static final String CREATE_USER_ID = "createUserId";
    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_USER_ID = "updateUserId";
    private static final String UPDATE_TIME = "updateTime";
    private static final String IS_DELETE = "isDelete";

    /**
     * 添加拦截
     *
     * @param metaObject 实例对象
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 获取创建时间
        Object createTime = this.getFieldValByName(CREATE_TIME, metaObject);
        if (createTime == null) {
            this.strictInsertFill(metaObject, CREATE_TIME, LocalDateTime.class, LocalDateTime.now());
        }
        // 是否删除
        Object isDelete = this.getFieldValByName(IS_DELETE, metaObject);
        if (isDelete == null) {
            this.strictInsertFill(metaObject, IS_DELETE, IsDeleteEnum.class, IsDeleteEnum.NO);
        }
        // 只有制定了创建人,而且没有指定修改人时,才会默认添加修改人信息
        Object createUserId = this.getFieldValByName(CREATE_USER_ID, metaObject);
        Object updateUserId = this.getFieldValByName(UPDATE_USER_ID, metaObject);
        if (updateUserId == null && createUserId != null) {
            this.strictInsertFill(metaObject, UPDATE_USER_ID, Long.class, Long.parseLong(createUserId + ""));
        }

        // 添加时指定初始修改人和修改时间
        this.updateFill(metaObject);
    }

    /**
     * 修改拦截
     *
     * @param metaObject 实例对象
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 获取修改时间
        Object updateTime = this.getFieldValByName(UPDATE_TIME, metaObject);
        if (updateTime == null) {
            this.strictInsertFill(metaObject, UPDATE_TIME, LocalDateTime.class, LocalDateTime.now());
        }
    }
}

9.配置显示时间格式

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * jackson序列化时间格式调整
 *
 * @author myl
 */
@Configuration
public class JacksonConfig {

    /**
     * 指定日期输出格式
     */
    private static final String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 定义时间序列化格式
     *
     * @return Jackson2ObjectMapperBuilderCustomizer
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customJackson() {
        return jacksonObjectMapperBuilder -> {
            // 针对于JDK新时间类。序列化时带有T的问题,自定义格式化字符串
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATA_FORMAT)));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATA_FORMAT)));
            jacksonObjectMapperBuilder.modules(javaTimeModule);
        };
    }
}

10.运行工具类

补充

实体类 时间格式为LocaDateTime

添加自动填充策略、逻辑删除策略、乐观锁注解

import com.baomidou.mybatisplus.annotation.*;

import java.time.LocalDateTime;
import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * <p>
 * 
 * </p>
 *
 * @author myl
 * @since 2022-06-20
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName("dept")
public class Dept implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    private Long deptno;


    private String dname;


    private String loc;

    

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUserId;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUserId;
    @Version
    private Integer version;

    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    private Integer isDelete;
}