Heycm

Heycm

整合Mybatis-Plus和代码生成器

2020-08-02

早就想写这个记录,主要为了记录MP常用配置以及代码生成器,也常用但总不记得配置,做个记录吧,一直拖着好久了。

MP官网:https://mp.baomidou.com/

官方也有很全的说明文档和demo,自行查阅,这里也就是将官方文档再说一遍,在此基础上提取一些通用配置形成配置文件,方便其他项目使用而已。


温馨提示:MP和SpringBoot有对应的版本要求,自行查看,maven仓库也可以看到一些。

一、版本、配置说明

JDK:1.8

SpringBoot:2.3.2.RELEASE

Mybatis-Plus:3.3.2

Mybatis-Plus Generator:3.3.2

Velocity:2.2(模板引擎,代码生成器需要)

数据库:MySQL5.7


二、数据表设计

6.png

create_time/create_user/modify_user/modify_time/is_deleted

作为公共字段,几乎每张表都有,当然有的表也可以没有,这要看设计,但是is_deleted是逻辑删除字段代表着该条记录是否删除,必须每张表都有。


三、创建项目,引入相关依赖

创建一个普通SpringBoot项目,以下是完整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.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>online.heycm</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-plus</name>
    <description>Mybatis Plus Demo for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <!-- 自定义工具包:统一返回对象 -->
        <dependency>
            <groupId>online.heycm</groupId>
            <artifactId>utils</artifactId>
            <version>0.0.3</version>
        </dependency>
        <!-- MySql数据库连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>

        <!-- MyBatis Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- MyBatis Plus 代码生成器 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

自定义工具包里主要有一些通用工具,以及统一返回对象,工具代码地址:https://github.com/heycm/demo/tree/master/utils


创建完成,项目结构如下图,习惯将配置文件重命名为yml

1.png


四、代码生成器

这里将生成器一些配置提取出来,放在配置文件。

1、创建配置文件,放资源文件夹下

2.png

# Mybatis Plus 代码生成器配置
# 生成模板输出路径
outPutDir=F:/github/demo/mybatis-plus/src/main/java
# 生成xml输出路径
xmlOutPutDir=F:/github/demo/mybatis-plus/src/main/resources/mapper
# 父包名
parentPackage=online.heycm.mybatisplus
# 作者
author=heycm
# 数据源
dataSource.driverName=com.mysql.cj.jdbc.Driver
dataSource.username=root
dataSource.password=root
dataSource.url=jdbc:mysql://localhost:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
# 表前缀,以","分割
tablePrefix=
# 字段前缀,以","分割
fieldPrefix=
# 是否生成swagger2注解
isUseSwagger2=false
# 使用lombok
isLombokModel=true
# 需要生成的表,以","分割
tables=permission,role,role_permission,user,user_role

2、创建utils包,创建代码生成器

3.png

package online.heycm.mybatisplus.utils;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
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 java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

public class CodeGenerator {

    public static void main(String[] args) {

        // 读取配置文件
        ResourceBundle rb = ResourceBundle.getBundle("mp-generator");

        // 实例一个代码生成器
        AutoGenerator ag = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(rb.getString("outPutDir")); // 输出路径
        gc.setFileOverride(true); // 同名覆盖
        gc.setActiveRecord(true); // 开启 activeRecord 模式
        gc.setEnableCache(false); // XML 二级缓存
        gc.setBaseResultMap(true); // XML ResultMap
        gc.setBaseColumnList(true); // XML columnList
        gc.setAuthor(rb.getString("author")); // 作者
        gc.setDateType(DateType.ONLY_DATE); // 时间类型对应策略
        gc.setSwagger2("true".equals(rb.getString("isUseSwagger2"))); // 实体属性 Swagger2 注解

        gc.setEntityName("%s"); // 实体命名方式,如 %sEntity 生成 UserEntity
        gc.setControllerName("%sController"); // controller命名方式,如 %sAction 生成 UserAction
        gc.setServiceName("%sService"); // service命名方式,如 %sBusiness 生成 UserBusiness
        gc.setServiceImplName(gc.getServiceName() + "Impl"); // service impl命名方式,如 %sBusinessImpl 生成 UserBusinessImpl
        gc.setMapperName("%sMapper"); // mapper命名方式,如 %sDao 生成 UserDao
        // gc.setXmlName("%sMapper"); // Mapper xml命名方式,如 %sDao 生成 UserDao.xml
        ag.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig ds = new DataSourceConfig();
        ds.setDbType(DbType.MYSQL); // 数据库类型
        ds.setTypeConvert(new MySqlTypeConvert()); // 类型转换
        ds.setDriverName(rb.getString("dataSource.driverName"));
        ds.setUsername(rb.getString("dataSource.username"));
        ds.setPassword(rb.getString("dataSource.password"));
        ds.setUrl(rb.getString("dataSource.url"));
        ag.setDataSource(ds);

        // 生成策略配置
        StrategyConfig sc = new StrategyConfig();
        sc.setTablePrefix(rb.getString("tablePrefix").split(",")); // 表前缀
        sc.setNaming(NamingStrategy.underline_to_camel); // 表名生成策略 t_user_xxx UserXxx,驼峰命名
        sc.setFieldPrefix(rb.getString("fieldPrefix").split(",")); // 字段前缀
        sc.setEntityLombokModel("true".equals(rb.getString("isLombokModel"))); // 实体用 lombok
        sc.setRestControllerStyle(true);//restful api

        sc.setInclude(rb.getString("tables").split(",")); // 需要生成的表
        /*
            逻辑删除列和自动填充列,需要看需求来定,可以有也可以没有
            如果有,还需要涉及数据表的设计
            若不需要,注释掉即可
         */
        sc.setLogicDeleteFieldName("is_deleted"); // 逻辑删除列
        List<TableFill> tableFillList = new ArrayList<TableFill>();//自动填充列配置
        tableFillList.add(new TableFill("is_deleted", FieldFill.INSERT)); // 插入时自动填充数据
        tableFillList.add(new TableFill("create_user", FieldFill.INSERT)); // 插入时自动填充数据
        tableFillList.add(new TableFill("create_time", FieldFill.INSERT)); // 插入时自动填充数据
        tableFillList.add(new TableFill("modify_user", FieldFill.INSERT_UPDATE)); // 插入和更新时自动填充数据
        tableFillList.add(new TableFill("modify_time", FieldFill.INSERT_UPDATE)); // 插入和更新时自动填充数据
        sc.setTableFillList(tableFillList);
        ag.setStrategy(sc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(rb.getString("parentPackage"));
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setEntity("model");
        pc.setMapper("mapper");
        ag.setPackageInfo(pc);

        //注入自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        ArrayList<FileOutConfig> focList = new ArrayList<>();
        // 调整 xml 生成目录
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return rb.getString("xmlOutPutDir") + "/" + tableInfo.getEntityName() + "Mapper.xml";
            }
        });
        cfg.setFileOutConfigList(focList);
        ag.setCfg(cfg);

        // 模板设置,null就不生成
        TemplateConfig tc = new TemplateConfig();
        tc.setController("/templates/controller.java.vm");
        tc.setService("/templates/service.java.vm");
        tc.setServiceImpl("/templates/serviceImpl.java.vm");
        tc.setEntity("/templates/entity.java.vm");
        tc.setMapper("/templates/mapper.java.vm");
        tc.setXml(null);
        ag.setTemplate(tc);

        // 执行
        ag.execute();
    }
}

这个代码生成器对应的是MySQL数据库,如果是Oracle或者其他,需要修改相应的的数据源配置。

3、执行程序,显示文件“=======文件生成完成!!!=======”说明成功

4.png

五、Mybatis Plus 启动配置

1、主配置文件配置,主要看 Mybatis Plus 配置

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/shiro?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
    username: root
    password: root
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      min-evictable-idle-time-millis: 600000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      keep-alive: true
      filters: stat,wall,slf4j


mybatis-plus:
  mapper-locations: classpath:mapper/*.xml # 映射文件位置
  global-config:
    db-config:
      id-type: auto # 自动递增ID
      logic-not-delete-value: 0 # 逻辑未删除
      logic-delete-value: 1     # 逻辑已删除
      update-strategy: not_empty # 字段策略,当字段值为null或""时,不更新
      insert-strategy: not_empty # 字段策略,当字段值为null或""时,不插入
      select-strategy: not_empty # 字段策略,当字段值为null或""时,不增加相应查询条件,要使用Wrapper.setEntity设置的查询条件,selectStrateg才会生效
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL


# 自定义配置
jwt:
  token:
    name: Authorization

2、在启动类扫描Mapper类 或者 在Mapper类加注解@Mapper,二选一

5.png


六、分页配置

创建 Mybatis Plus 配置类,内容如下

package online.heycm.mybatisplus.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    /**
     * 分页配置
     * 配置bean,将逻辑分页变为物理分页
     */
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }

}

七、字段自动填充配置

创建字段自动填充配置类,内容如下

package online.heycm.mybatisplus.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Component
public class AutoFillHandler implements MetaObjectHandler {

    @Value("${jwt.token.name}")
    private String jwtTokenName;

    @Autowired
    HttpServletRequest request;

    /**
     * 插入填充策略
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        //判断表里是否有setIsDeleted方法,如果有可以再去加自动填充,因为我们所有表都有,所以不加判断
        //boolean hasSetter = metaObject.hasSetter("isDeleted");
        setInsertFieldValByName("isDeleted", 0, metaObject);
        setInsertFieldValByName("createTime", new Date(), metaObject);
        setInsertFieldValByName("modifyTime", new Date(), metaObject);
        setInsertFieldValByName("createUser", getIdByToken(), metaObject);
        setInsertFieldValByName("modifyUser", getIdByToken(), metaObject);
    }

    /**
     * 更新填充策略
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 判断是否已设置修改人,若已设置,此处不再填充
        Object modifyUser = getFieldValByName("modifyUser", metaObject);
        if(modifyUser == null) {
            setUpdateFieldValByName("modifyUser", getIdByToken(), metaObject);
        }
        setUpdateFieldValByName("modifyTime", new Date(), metaObject);
    }

    /**
     * 获取当前用户ID
     */
    private Integer getIdByToken() {
        String token = request.getHeader(jwtTokenName);
        
        // 打印token
        System.out.println("=======>token: " + token); 
        
        if (token == null) {
            return null;
        }
        // TODO 解析JWT,获取JTW中的用户ID
        return 1;
    }
}

八、模拟测试

1、写接口

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/save")
    public ResModel save(@RequestBody User user) {
        // 有ID执行更新,无ID执行插入
        // 测试 update-strategy、insert-strategy、字段填充
        boolean isOk = user.insertOrUpdate();
        return Result.apiRes(isOk, "成功", "失败");
    }

    @PostMapping("/query")
    public ResModel query(@RequestBody User user) {
        // 按条件查询
        // 测试 select-strategy
        User one = user.selectOne(new QueryWrapper<User>().setEntity(user));
        return Result.apiRes(one != null, one, "查无此人");
    }

}

2、测试

插入测试:字段为""或null时不插入,字段自动插入策略

发送请求

7.png

请求结果

8.png

控制台打印

9.png

数据库

10.png


更新测试:字段为""或null时不更新,字段自动更新策略,获取token

发送请求

11.png

请求结果

12.png

控制台打印

13.png

数据库

14.png


查询测试:当字段值为null或""时,不增加相应查询条件

发送请求

15.png

请求结果

16.png

控制台打印

17.png


发送请求

18.png

请求结果

19.png

控制台打印

20.png




更多用法参考官网,很详细。




代码:https://github.com/heycm/demo/tree/master/mybatis-plus