从零搭建SpringBoot后台框架(十一)——自定义freemarker模板

195 阅读8分钟

一、本文内容

  1. 上文出入参、converter、controller自动生成后,都进行了手动调整,本文将这些手动调整的内容,整合为自动生成代码
  2. 解决swagger刷新报错问题

二、解决swagger刷新报错问题

添加依赖,指定swagger-models版本为1.5.22,默认为1.5.20

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.22</version>
        </dependency>

三、修改MybatisPlusGenerator配置

  1. 添加@FixMethodOrder(MethodSorters.NAME_ASCENDING)控制测试方法执行顺序
  2. StrategyConfig初始化改为私有方法,在setUp方法调用,保证每个测试用例执行前重新初始化一遍,避免测试用例赋值的相互影响
  3. 标准生成方法禁用controller,单独写用例实现个性化定义
  4. 自定义SaveCommand生成模板,指定包名model.command,指定模板templates/entitySaveCommand.java,指定类名entityName + "SaveCommand",忽略字段"id", "version", "delete_flag", "create_time", "update_time", "updated_by"
  5. 自定义UpdateCommand生成模板,控制bigint生成String类型,指定包名model.command,指定模板templates/entityUpdateCommand.java,指定类名entityName + "UpdateCommand",忽略字段"delete_flag", "create_time", "created_by", "update_time"
  6. 自定义DetailResp生成模板,控制bigint生成String类型,指定包名model.resp,指定模板templates/entityDetailResp.java,指定类名entityName + "DetailResp",忽略字段"delete_flag",情况version乐观锁版本设置
  7. 自定义PageResp生成模板,控制bigint生成String类型,指定包名model.resp,指定模板templates/entityPageResp.java,指定类名entityName + "PageResp",忽略字段"delete_flag",情况version乐观锁版本设置
  8. 自定义Converter生成模板,指定包名model.converter,指定模板templates/converter.java,指定类名entityName + "Converter",复用Controller的生成方法
  9. 自定义Controller生成模板,指定包名controller,指定模板templates/controller.java,指定类名entityName + "Converter",这里仅生成controller类
package com.example.demo.config;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

import java.sql.Types;
import java.util.*;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MybatisPlusGenerator {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/demo?useSSL=false";
    private static final String DB_USERNAME = "root";
    private static final String DB_PASSWORD = "123456";
    private static final List<String> TABLES = Arrays.asList(
            //"user_info",
            "system_log"
    );
    private static final String AUTHOR = "chenzl";
    private static final String PKG_PARENT = "com.example.demo";
    private static final String PKG_ENTITY = "model";
    private static final String PKG_MAPPER = "dao";

    /**
     * 数据源配置
     */
    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(DB_URL, DB_USERNAME, DB_PASSWORD)
            //数据库 schema(部分数据库适用)  mybatis-plus
            //.schema("demo")
            .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                if (typeCode == Types.SMALLINT) {
                    // 自定义类型转换
                    return DbColumnType.INTEGER;
                }
                return typeRegistry.getColumnType(metaInfo);
            })
            .build();

    private static final GlobalConfig GLOBAL_CONFIG = new GlobalConfig.Builder()
            //禁止打开输出目录 默认值:true
            .disableOpenDir()
            //指定输出目录
            .outputDir(System.getProperty("user.dir") + "/src/main/java")
            //设置作者
            .author(AUTHOR)
            //开启swagger模式
            .enableSwagger()
            //时间策略 DateType.ONLY_DATE 默认值: DateType.TIME_PACK
            //.dateType(DateType.TIME_PACK)
            //注释日期 默认值: yyyy-MM-dd
            //.commentDate("yyyy-MM-dd HH:mm:ss")
            .build();

    private static final PackageConfig PACKAGE_CONFIG = new PackageConfig.Builder()
            //设置父包名
            .parent(PKG_PARENT)
            //父包模块名    默认值:无
            //.moduleName("demo")
            .entity(PKG_ENTITY)
            //Mapper包名 默认值:mapper
            .mapper(PKG_MAPPER)
            //设置mapperXml生成路径
            .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper"))
            .build();

    private StrategyConfig strategyConfig;

    private void initStrategyConfig() {
        strategyConfig = new StrategyConfig.Builder()
                // 设置需要生成的表名
                .addInclude(TABLES)
                // 设置过滤表前缀
                //.addTablePrefix("t_")

                //controller配置
                .controllerBuilder()
                //覆盖已生成文件
                .enableFileOverride()
                //开启生成@RestController 控制器  默认值:false
                .enableRestStyle()

                //service配置
                .serviceBuilder()
                //覆盖已生成文件
                .enableFileOverride()
                .convertServiceFileName((entityName) -> entityName + "Service")
                .convertServiceImplFileName((entityName) -> entityName + "ServiceImpl")

                //mapper配置
                .mapperBuilder()
                //覆盖已生成文件
                .enableFileOverride()
                //启用 BaseResultMap 生成  默认值:false
                .enableBaseResultMap()
                //启用 BaseColumnList    默认值:false
                .enableBaseColumnList()
                //转换 mapper 类文件名称   默认值:%sMapper
                //.convertMapperFileName((entityName) -> entityName + "Mapper")
                //转换 xml 文件名称  默认值:%sMapper
                //.convertXmlFileName((entityName) -> entityName + "Mapper")

                //entity配置
                .entityBuilder()
                //覆盖已生成文件
                .enableFileOverride()
                //禁用生成 serialVersionUID    默认值:true
                .disableSerialVersionUID()
                //开启lombok模型   默认值:false
                .enableLombok()
                //开启Boolean类型字段移除is前缀  默认值:false mysql 数据库字段类型为tinyint(1)时,会自动转换为Boolean类型
                .enableRemoveIsPrefix()
                //乐观锁字段名(数据库字段)    versionColumnName与versionPropertyName二选一即可
                .versionColumnName("version")
                //逻辑删除字段名(数据库字段)   logicDeleteColumnName与logicDeletePropertyName二选一即可
                .logicDeleteColumnName("delete_flag")
                //转换文件名称
                //.convertFileName((entityName) -> entityName + "PO")
                //格式化文件名称
                //.formatFileName("%sPO")
                //全局主键类型
                .idType(IdType.ASSIGN_ID)
                .addTableFills(Arrays.asList(
                        new Column("create_time", FieldFill.INSERT),
                        new Column("created_by", FieldFill.INSERT),
                        new Column("update_time", FieldFill.INSERT_UPDATE),
                        new Column("updated_by", FieldFill.INSERT_UPDATE)
                ))

                .build();
    }

    @Before
    public void setUp() {
        initStrategyConfig();
    }

    @Test
    public void execute01() {
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(PACKAGE_CONFIG);
        generator.template(new TemplateConfig.Builder()
                .disable(TemplateType.CONTROLLER)
                .build());
        generator.strategy(strategyConfig);

        // 使用Freemarker引擎模板,默认的是Velocity引擎模板
        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute02_SaveCommand() {
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                //父包模块名    默认值:无
                .entity("model.command")
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .entity("templates/entitySaveCommand.java")
                .build());

        strategyConfig.entity().getTableFillList().clear();
        generator.strategy(strategyConfig.entityBuilder()
                .convertFileName((entityName) -> entityName + "SaveCommand")
                .addIgnoreColumns("id", "version", "delete_flag", "create_time", "update_time", "updated_by")
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute03_UpdateCommand() {
        DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(DB_URL, DB_USERNAME, DB_PASSWORD)
                .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    if (typeCode == Types.BIGINT) {
                        // 自定义类型转换
                        return DbColumnType.STRING;
                    }
                    return typeRegistry.getColumnType(metaInfo);
                })
                .build();
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                //父包模块名    默认值:无
                .entity("model.command")
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .entity("templates/entityUpdateCommand.java")
                .build());

        strategyConfig.entity().getTableFillList().clear();
        generator.strategy(strategyConfig.entityBuilder()
                .convertFileName((entityName) -> entityName + "UpdateCommand")
                .addIgnoreColumns("delete_flag", "create_time", "created_by", "update_time")
                //清空标准设置中的version字段
                .versionColumnName("")
                .idType(null)
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute04_DetailResp() {
        DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(DB_URL, DB_USERNAME, DB_PASSWORD)
                .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    if (typeCode == Types.BIGINT) {
                        // 自定义类型转换
                        return DbColumnType.STRING;
                    }
                    return typeRegistry.getColumnType(metaInfo);
                })
                .build();
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                //父包模块名    默认值:无
                .entity("model.resp")
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .entity("templates/entityDetailResp.java")
                .build());

        strategyConfig.entity().getTableFillList().clear();
        generator.strategy(strategyConfig.entityBuilder()
                .convertFileName((entityName) -> entityName + "DetailResp")
                .addIgnoreColumns("delete_flag")
                //清空标准设置中的version字段
                .versionColumnName("")
                .idType(null)
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute05_PageResp() {
        DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(DB_URL, DB_USERNAME, DB_PASSWORD)
                .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    if (typeCode == Types.BIGINT) {
                        // 自定义类型转换
                        return DbColumnType.STRING;
                    }
                    return typeRegistry.getColumnType(metaInfo);
                })
                .build();
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                //父包模块名    默认值:无
                .entity("model.resp")
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .entity("templates/entityPageResp.java")
                .build());

        strategyConfig.entity().getTableFillList().clear();
        generator.strategy(strategyConfig.entityBuilder()
                .convertFileName((entityName) -> entityName + "PageResp")
                .addIgnoreColumns("delete_flag")
                //清空标准设置中的version字段
                .versionColumnName("")
                .idType(null)
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute06_converter() {
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                .entity(PKG_ENTITY)
                //父包模块名    默认值:无
                .controller("model.converter")
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .controller("templates/converter.java")
                .build());

        generator.strategy(strategyConfig.controllerBuilder()
                .convertFileName((entityName) -> entityName + "Converter")
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }

    @Test
    public void execute07_controller() {
        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
        generator.global(GLOBAL_CONFIG);
        generator.packageInfo(new PackageConfig.Builder()
                //设置父包名
                .parent(PKG_PARENT)
                .entity(PKG_ENTITY)
                .build());
        generator.template(new TemplateConfig.Builder()
                .disable()
                .controller("templates/controller.java")
                .build());

        generator.strategy(strategyConfig.controllerBuilder()
                .build());

        generator.execute(new FreemarkerTemplateEngine());
    }
}

四、自定义freemarker模板

1.entitySaveCommand.java.ftl

package ${package.Entity};

<#if springdoc>
import io.swagger.v3.oas.annotations.media.Schema;
<#elseif swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

/**
 * <p>
 * ${table.comment!}新增入参
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Getter
@Setter
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if springdoc>
@Schema(name = "${entity}", description = "${table.comment!}")
<#elseif swagger>
@ApiModel(value = "${entity}", description = "${table.comment!}新增入参")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>

    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if springdoc>
    @Schema(description = "${field.comment}")
        <#elseif swagger>
    @ApiModelProperty("${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>
<#if entityColumnConstant>
    <#list table.fields as field>

    public static final String ${field.name?upper_case} = "${field.name}";
    </#list>
</#if>
<#if activeRecord>

    @Override
    public Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }
</#if>
<#if !entityLombokModel>

    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName} = " + ${field.propertyName} +
        <#else>
            ", ${field.propertyName} = " + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

2.entityUpdateCommand.java.ftl

package ${package.Entity};

<#if springdoc>
import io.swagger.v3.oas.annotations.media.Schema;
<#elseif swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

import javax.validation.constraints.NotBlank;

/**
 * <p>
 * ${table.comment!}修改入参
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Getter
@Setter
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if springdoc>
@Schema(name = "${entity}", description = "${table.comment!}")
<#elseif swagger>
@ApiModel(value = "${entity}", description = "${table.comment!}修改入参")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>

    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if springdoc>
    @Schema(description = "${field.comment}")
        <#elseif swagger>
    @ApiModelProperty("${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    <#if field.propertyName == "id">
    @NotBlank(message = "id不能为空")
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>
<#if entityColumnConstant>
    <#list table.fields as field>

    public static final String ${field.name?upper_case} = "${field.name}";
    </#list>
</#if>
<#if activeRecord>

    @Override
    public Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }
</#if>
<#if !entityLombokModel>

    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName} = " + ${field.propertyName} +
        <#else>
            ", ${field.propertyName} = " + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

3.entityDetailResp.java.ftl

package ${package.Entity};

<#if springdoc>
import io.swagger.v3.oas.annotations.media.Schema;
<#elseif swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

import java.time.LocalDateTime;
/**
 * <p>
 * ${table.comment!}详情反参
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Getter
@Setter
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if springdoc>
@Schema(name = "${entity}", description = "${table.comment!}")
<#elseif swagger>
@ApiModel(value = "${entity}", description = "${table.comment!}详情反参")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>

    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if springdoc>
    @Schema(description = "${field.comment}")
        <#elseif swagger>
    @ApiModelProperty("${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>
<#if entityColumnConstant>
    <#list table.fields as field>

    public static final String ${field.name?upper_case} = "${field.name}";
    </#list>
</#if>
<#if activeRecord>

    @Override
    public Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }
</#if>
<#if !entityLombokModel>

    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName} = " + ${field.propertyName} +
        <#else>
            ", ${field.propertyName} = " + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

4.entityPageResp.java.ftl

package ${package.Entity};

<#if springdoc>
import io.swagger.v3.oas.annotations.media.Schema;
<#elseif swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

import java.time.LocalDateTime;
/**
 * <p>
 * ${table.comment!}分页反参
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Getter
@Setter
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if springdoc>
@Schema(name = "${entity}", description = "${table.comment!}")
<#elseif swagger>
@ApiModel(value = "${entity}", description = "${table.comment!}分页反参")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>

    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if springdoc>
    @Schema(description = "${field.comment}")
        <#elseif swagger>
    @ApiModelProperty("${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>

    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>
<#if entityColumnConstant>
    <#list table.fields as field>

    public static final String ${field.name?upper_case} = "${field.name}";
    </#list>
</#if>
<#if activeRecord>

    @Override
    public Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }
</#if>
<#if !entityLombokModel>

    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName} = " + ${field.propertyName} +
        <#else>
            ", ${field.propertyName} = " + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

5.converter.java.ftl

package ${package.Controller};

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Entity}.${entity};
import ${package.Entity}.command.${entity}SaveCommand;
import ${package.Entity}.command.${entity}UpdateCommand;
import ${package.Entity}.resp.${entity}DetailResp;
import ${package.Entity}.resp.${entity}PageResp;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

/**
* <p>
    * ${table.comment!} 转换器
    * </p>
*
* @author ${author}
* @since ${date}
*/
@Mapper
public interface ${entity}Converter {
    ${entity}Converter INSTANCE = Mappers.getMapper(${entity}Converter.class);

    ${entity} convert(${entity}SaveCommand source);

    ${entity} convert(${entity}UpdateCommand source);

    ${entity}DetailResp convert2DetailResp(${entity} source);

    Page<${entity}PageResp> convert2PageResp(Page<${entity}> source);
}

6.修改controller.java.ftl

package ${package.Controller};

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.http.BusinessException;
import com.example.demo.common.http.RestResult;
import ${package.Entity}.${entity};
import ${package.Entity}.command.${entity}SaveCommand;
import ${package.Entity}.command.${entity}UpdateCommand;
import ${package.Entity}.converter.${entity}Converter;
import ${package.Entity}.resp.${entity}DetailResp;
import ${package.Entity}.resp.${entity}PageResp;
import ${package.Service}.${table.serviceName};
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

import javax.annotation.Resource;

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
    class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
@Api(tags = "${table.comment!}")
    <#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
    <#else>
public class ${table.controllerName} {
    </#if>

    @Resource
    private ${table.serviceName} ${table.serviceName?uncap_first};

    @PostMapping("/save")
    @ApiOperation(value = "新增${table.comment!}")
    public RestResult<String> save(@RequestBody @Validated ${entity}SaveCommand command) {
        ${entity} ${entity?uncap_first} = ${entity}Converter.INSTANCE.convert(command);
        ${table.serviceName?uncap_first}.save(${entity?uncap_first});
        return RestResult.getSuccessResult(String.valueOf(${entity?uncap_first}.getId()));
    }

    @PostMapping("/removeById")
    @ApiOperation(value = "根据id删除${table.comment!}")
    @ApiImplicitParams(
            @ApiImplicitParam(name = "id", value = "主键id", required = true, dataType = "String")
    )
    public RestResult<Boolean> removeById(String id) {
        boolean success = ${table.serviceName?uncap_first}.removeById(Long.parseLong(id));
        return RestResult.getSuccessResult(success);
    }

    @PostMapping("/updateById")
    @ApiOperation(value = "根据id更新${table.comment!}")
    public RestResult<Boolean> updateById(@RequestBody @Validated ${entity}UpdateCommand command) {
        ${entity} ${entity?uncap_first} = ${entity}Converter.INSTANCE.convert(command);
        boolean success = ${table.serviceName?uncap_first}.updateById(${entity?uncap_first});
        return RestResult.getSuccessResult(success);
    }

    @PostMapping("/getById")
    @ApiOperation(value = "根据id查询${table.comment!}")
    @ApiImplicitParams(
            @ApiImplicitParam(name = "id", value = "主键id", required = true, dataType = "String")
    )
    public RestResult<${entity}DetailResp> getById(String id) {
        ${entity} ${entity?uncap_first} = ${table.serviceName?uncap_first}.getById(Long.parseLong(id));
        if (null == ${entity?uncap_first}) {
            throw new BusinessException("id不存在");
        }
        ${entity}DetailResp resp = ${entity}Converter.INSTANCE.convert2DetailResp(${entity?uncap_first});
        return RestResult.getSuccessResult(resp);
    }

    @PostMapping("/page")
    @ApiOperation(value = "分页查询${table.comment!}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "pageNum", value = "当前页码", dataType = "Integer", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "每页显示条数", dataType = "Integer", paramType = "query")
    })
    public RestResult<Page<${entity}PageResp>> page(Integer pageNum, Integer pageSize) {
        Page<${entity}> page = ${table.serviceName?uncap_first}.page(new Page<>(pageNum, pageSize));
        Page<${entity}PageResp> resp = ${entity}Converter.INSTANCE.convert2PageResp(page);
        return RestResult.getSuccessResult(resp);
    }
}
</#if>

五、功能验证

  1. 执行MybatisPlusGenerator类,可以看到用例均执行成功,重新生成了出入参、controller、service、mapper
  2. 表设置新增user_info,可以生成userInfo相关内容

image.png

最新建表SQL

CREATE TABLE `system_log` (
                              `id` bigint(20) NOT NULL COMMENT '主键id',
                              `description` varchar(50) DEFAULT NULL COMMENT '日志信息描述',
                              `method` varchar(20) DEFAULT NULL COMMENT '方法名称',
                              `log_type` varchar(10) DEFAULT NULL COMMENT '日志类型 0是正常,1是异常',
                              `request_ip` varchar(30) DEFAULT NULL COMMENT '请求的ip',
                              `exception_code` varchar(50) DEFAULT NULL COMMENT '异常错误码',
                              `exception_detail` varchar(255) DEFAULT NULL COMMENT '异常详情',
                              `params` varchar(1000) DEFAULT NULL COMMENT '请求参数',
                              `user_id` varchar(32) DEFAULT NULL COMMENT '请求的用户id',
                              `delete_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除:0-否,1-是',
                              `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
                              `created_by` varchar(128) DEFAULT '' COMMENT '创建人',
                              `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
                              `updated_by` varchar(128) DEFAULT '' COMMENT '最后修改人',
                              `version` int(8) DEFAULT '0' COMMENT '版本号',
                              PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统日志表';

CREATE TABLE `user_info` (
                             `id` bigint(20) NOT NULL COMMENT '主键id',
                             `user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
                             `password` varchar(100) DEFAULT NULL COMMENT '用户密码',
                             `avatar` varchar(100) DEFAULT NULL COMMENT '图像',
                             `is_vip` tinyint(1) DEFAULT '0' COMMENT '是否VIP',
                             `delete_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除:0-否,1-是',
                             `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
                             `created_by` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '' COMMENT '创建人',
                             `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
                             `updated_by` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '' COMMENT '最后修改人',
                             `version` int(8) DEFAULT '0' COMMENT '版本号',
                             PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';

六、项目地址

gitee

PS:可以通过tag下载本文对应的代码版本

七、结尾

自定义freemarker模板已完成,有问题可以联系chenzhenlindx@qq.com

八、参考文章

  1. 从零搭建自己的SpringBoot后台框架(十)
  2. 代码生成器配置新