Bootstrap

MyBatis Plus Generator 代码生成器 v3.5.x 案例,含校验、MapStruct、Swagger、QO、VO,自定义 FreeMarker 模板引擎

v3.5.3、v3.5.3.1

详情请点击

v3.5.1

代码生成器

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.irms.config.mybatis.MyFreemarkerTemplateEngine;
import com.irms.controller.BaseController;
import com.irms.domain.entity.BaseEntity;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.function.BiConsumer;

/**
 * 代码生成器:https://github.com/baomidou/generator/blob/develop/mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/SimpleAutoGenerator.java
 *
 * @author duanluan
 */
public class CodeGenerator {

  /**
   * 读取控制台输入内容
   */
  private static final Scanner SCANNER = new Scanner(System.in);

  /**
   * 控制台输入内容读取并打印提示信息
   *
   * @param message 提示信息
   * @return
   */
  public static String scannerNext(String message) {
    System.out.println(message);
    String nextLine = SCANNER.nextLine();
    if (StringUtils.isBlank(nextLine)) {
      // 如果输入空行继续等待
      return SCANNER.next();
    }
    return nextLine;
  }

  protected static <T> T configBuilder(IConfigBuilder<T> configBuilder) {
    return null == configBuilder ? null : configBuilder.build();
  }

  public static void main(String[] args) {
    // 自定义模板,key 为 自定义的包名:Entity 后缀,value 为模板路径
    Map<String, String> customFile = new HashMap<>();
    customFile.put("domain/qo:GetQO", "/template/generator/entityQO.java");
    customFile.put("domain/qo:ListQO", "/template/generator/entityQO.java");
    customFile.put("domain/qo:SaveQO", "/template/generator/entityQO.java");
    customFile.put("domain/qo:UpdateQO", "/template/generator/entityQO.java");
    customFile.put("domain/qo:RemoveQO", "/template/generator/entityQO.java");

    customFile.put("domain/vo:ListVO", "/template/generator/entityVO.java");
    customFile.put("domain/vo:GetVO", "/template/generator/entityVO.java");

    customFile.put("struct:Struct", "/template/generator/struct.java");

    // 代码生成器
    new AutoGenerator(configBuilder(new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/xxx?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC", "root", "")))
      // 全局配置
      .global(configBuilder(new GlobalConfig.Builder()
        // 覆盖已生成文件,默认 false
        .fileOverride()
        // 禁用打开生成目录
        // .disableOpenDir()
        // 输出目录,默认 windows: D://  linux or mac: /tmp
        .outputDir("/generator/src/main/java")
        // .outputDir(System.getProperty("user.dir") + "/generator/src/main/java")
        // 作者,默认无
        // .author("")
        // 注释时间(@since),默认 yyyy-MM-dd
        .commentDate("")
        // 开启 swagger 模式,默认 false
        .enableSwagger()
      ))
      // 包配置
      .packageInfo(configBuilder(new PackageConfig.Builder()
        // 模块名
        .moduleName("")
        // 实体包名
        .entity("domain")
        // 父包名
        .parent("top.zhogjianhao")
      ))
      // 自定义配置
      .injection(configBuilder(new InjectionConfig.Builder()
        .beforeOutputFile(new BiConsumer<TableInfo, Map<String, Object>>() {
          @Override
          public void accept(TableInfo tableInfo, Map<String, Object> stringObjectMap) {
            // 不启用 @TableName 注解
            // tableInfo.setConvert(false);

            // 自定义 Mapper XML 生成目录
            ConfigBuilder config = (ConfigBuilder) stringObjectMap.get("config");
            Map<OutputFile, String> pathInfoMap = config.getPathInfo();
            pathInfoMap.put(OutputFile.mapperXml, pathInfoMap.get(OutputFile.mapperXml).replaceAll("/java.*", "/resources/mapper"));
            stringObjectMap.put("config", config);
          }
        })
        // 自定义文件,比如 VO
        .customFile(customFile)
      ))
      // 策略配置
      .strategy(configBuilder(new StrategyConfig.Builder()
        // 表名
        .addInclude(scannerNext("请输入表名(英文逗号分隔):").split(","))
        // 过滤表前缀
        .addTablePrefix("t_")

        // Entity 策略配置
        .entityBuilder()
        // 开启 Lombok 模式
        .enableLombok()
        // 禁用生成 serialVersionUID
        .disableSerialVersionUID()
        // 数据库表映射到实体的命名策略:下划线转驼峰
        .naming(NamingStrategy.underline_to_camel)
        // 主键策略为自增,默认 IdType.AUTO
        .idType(IdType.AUTO)
        // 父类
        .superClass(BaseEntity.class)

        // Controller 策略配置
        .controllerBuilder()
        // 生成 @RestController 注解
        .enableRestStyle()
        // 父类
        .superClass(BaseController.class)
      ))
      // 模板配置
      .template(configBuilder(new TemplateConfig.Builder()
        // 自定义模板:https://github.com/baomidou/generator/tree/develop/mybatis-plus-generator/src/main/resources/templates
        .entity("/template/generator/entity.java")
        .mapper("/template/generator/mapper.java")
        .service("/template/generator/service.java")
        .serviceImpl("/template/generator/serviceImpl.java")
        .controller("/template/generator/controller.java")
      ))

      // 执行并指定模板引擎
      .execute(new MyFreemarkerTemplateEngine());
    // .execute(new FreemarkerTemplateEngine());
  }
}


自定义模板引擎(FreeMarker)

import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.Map;

/**
 * 自定义模板引擎处理,用于生成 QO、VO 等
 */
public class MyFreemarkerTemplateEngine extends FreemarkerTemplateEngine {

  @Override
  protected void outputCustomFile(Map<String, String> customFile, TableInfo tableInfo, Map<String, Object> objectMap) {
    String entityName = tableInfo.getEntityName();
    String otherPath = this.getPathInfo(OutputFile.other);
    customFile.forEach((key, value) -> {
      // 根据自定义路径替换 other 路径
      String[] keys = key.split(":");
      if (keys.length > 1) {
        key = keys[1];
      }
      String fileName = String.format((keys.length > 1 ? otherPath.replace("\\other", "\\" + keys[0]) : otherPath) + File.separator + entityName + "%s.java", key);
      objectMap.put("fileNameSuffix", key);

      // 处理路由名,结尾加 s
      String[] controllerMappingHyphens = objectMap.get("controllerMappingHyphen").toString().split("-");
      if (controllerMappingHyphens.length > 1) {
        StringBuilder controllerMappingHyphenStr = new StringBuilder(controllerMappingHyphens[0]);
        for (int i = 1; i < controllerMappingHyphens.length; i++) {
          controllerMappingHyphenStr.append(StringUtils.capitalize(controllerMappingHyphens[i]));
        }
        objectMap.put("controllerMappingHyphen", controllerMappingHyphenStr.append("s").toString());
      }

      this.outputFile(new File(fileName), objectMap, value + ".ftl");
    });
  }
}

自定义模板 RESTful(FreeMarker)src/main/resources/template/generator

controller.java.ftl

package ${package.Controller};

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import ${package.Entity}.${entity};
import ${package.Entity}.qo.*;
import ${package.Entity}.vo.*;
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>
import ${package.Service}.${table.serviceName};
import ${package.Parent}.struct.${entity}Struct;
import ${package.Parent}.tools.domain.response.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

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

  @Autowired
  private ${table.serviceName} ${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)};

  @ApiOperation(value = "${table.comment!}列表", notes = "${table.comment!}列表")
  @GetMapping
  public Result<List<${entity}ListVO>> list(@Validated ${entity}ListQO query) {
    try {
      startPage(query);
      return getTableList(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.list(new LambdaQueryWrapper<${entity}>()
        .orderByDesc(${entity}::getUpdatedTime)
      ));
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return failed("${table.comment!}列表查询异常", e);
    }
  }

  @ApiOperation(value = "${table.comment!}详情", notes = "${table.comment!}详情")
  @GetMapping("/{id}")
  public Result<${entity}GetVO> get(@Validated ${entity}GetQO query) {
    try {
      return ok(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.getById(query.getId()));
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return failed("${table.comment!}详情查询异常", e);
    }
  }

  @ApiOperation(value = "保存${table.comment!}", notes = "保存${table.comment!}")
  @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
  @PostMapping
  public Result save(@RequestBody @Validated ${entity}SaveQO obj) {
    try {
      return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.save(${entity}Struct.INSTANCE.to(obj)), SAVE_FAILED_MSG);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return failed("${table.comment!}保存异常", e);
    }
  }

  @ApiOperation(value = "更新${table.comment!}", notes = "更新${table.comment!}")
  @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
  @PutMapping("/{id}")
  public Result update(@RequestBody @Validated ${entity}UpdateQO obj) {
    try {
      return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.updateById(${entity}Struct.INSTANCE.to(obj)), UPDATE_FAILED_MSG);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return failed("${table.comment!}更新异常", e);
    }
  }

  @ApiOperation(value = "删除${table.comment!}", notes = "删除${table.comment!}")
  @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
  @DeleteMapping("/{ids}")
  public Result remove(@Validated ${entity}RemoveQO query) {
    try {
      return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.removeByIds(Arrays.asList(query.getIds().split(","))), REMOVE_FAILED_MSG);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return failed("${table.comment!}删除异常", e);
    }
  }
}
</#if>

entity.java.ftl

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#--<#if swagger2??>-->
<#--import io.swagger.annotations.ApiModel;-->
<#--import io.swagger.annotations.ApiModelProperty;-->
<#--</#if>-->
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
  <#if chainModel>
import lombok.experimental.Accessors;
  </#if>
</#if>

/**
 * ${table.comment!}
 *
<#if author != "">
 * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
<#if table.convert>
@TableName("${table.name}")
</#if>
<#if entityLombokModel>
  <#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
  <#else>
@EqualsAndHashCode(callSuper = false)
  </#if>
  <#if chainModel>
@Accessors(chain = true)
  </#if>
@Data
</#if>
<#--<#if swagger2??>-->
<#--@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}> {
<#else>
public class ${entity} <#if entitySerialVersionUID>implements Serializable</#if> {
</#if>

<#if entitySerialVersionUID>
  private static final long serialVersionUID = 1L;

</#if>
<#-- ----------  BEGIN 字段循环遍历  -------- -->
<#list table.fields as field>
  <#-- 排除公共字段 -->
<#--  <#if field.propertyName != 'id' && field.propertyName != 'delFlag' && field.propertyName != 'createdTime' && field.propertyName != 'createdBy' && field.propertyName != 'updatedTime' && field.propertyName != 'updatedBy' && field.propertyName != 'remark'>-->
  <#if field.keyFlag>
    <#assign keyPropertyName="${field.propertyName}"/>
  </#if>
  <#if field.comment!?length gt 0>
<#--    <#if swagger2??>-->
<#--  @ApiModelProperty("${field.comment}")-->
<#--    <#else>-->
  /**
   * ${field.comment}
   */
<#--    </#if>-->
  </#if>
  <#if field.keyFlag>
    <#-- 主键 -->
    <#if field.keyIdentityFlag>
  @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
    <#elseif idType??>
  @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
    <#elseif field.convert>
  @TableId("${field.annotationColumnName}")
    </#if>
    <#-- 普通字段 -->
  <#elseif field.fill??>
  <#-- -----   存在字段填充设置   ----->
    <#if field.convert>
  @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
    <#else>
  @TableField(fill = FieldFill.${field.fill})
    </#if>
  <#elseif field.convert>
  @TableField("${field.annotationColumnName}")
  </#if>
  <#-- 乐观锁注解 -->
  <#if field.versionField>
  @Version
  </#if>
  <#-- 逻辑删除注解 -->
  <#if field.logicDeleteField>
  @TableLogic
  </#if>
  private ${field.propertyType} ${field.propertyName};
<#--  </#if>-->
</#list>
<#-- ----------  END 字段循环遍历  -------- -->
<#-- Lombok 模式 -->
<#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
  protected 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>
}

entityQO.java.ftl,生成后需自己调整字段

package ${package.Entity}.qo;

<#if fileNameSuffix?contains('ListQO')>
import com.irms.domain.entity.PageLimit;
</#if>
<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger??>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
  <#if chainModel>
import lombok.experimental.Accessors;
  </#if>
</#if>

import javax.validation.constraints.*;

/**
 * ${table.comment!}<#if fileNameSuffix?contains('List')>列表查询对象<#elseif fileNameSuffix?contains('Get')>详情查询对象<#elseif fileNameSuffix?contains('Save')>保存对象<#elseif fileNameSuffix?contains('Update')>更新对象<#elseif fileNameSuffix?contains('Remove')>删除对象<#else></#if>
 *
<#if author != "">
 * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
<#if entityLombokModel>
  <#if fileNameSuffix?contains('ListQO')>
@EqualsAndHashCode(callSuper = true)
  <#else>
@EqualsAndHashCode(callSuper = false)
  </#if>
  <#if chainModel>
@Accessors(chain = true)
  </#if>
@Data
</#if>
<#if swagger??>
@ApiModel(value = "${entity} <#if fileNameSuffix?contains('List')>列表查询对象<#elseif fileNameSuffix?contains('Get')>详情查询对象<#elseif fileNameSuffix?contains('Save')>保存对象<#elseif fileNameSuffix?contains('Update')>更新对象<#elseif fileNameSuffix?contains('Remove')>删除对象<#else></#if>", description = "${table.comment!}<#if fileNameSuffix?contains('List')>列表查询对象<#elseif fileNameSuffix?contains('Get')>详情查询对象<#elseif fileNameSuffix?contains('Save')>保存对象<#elseif fileNameSuffix?contains('Update')>更新对象<#elseif fileNameSuffix?contains('Remove')>删除对象<#else></#if>")
</#if>
<#if fileNameSuffix?contains("ListQO")>
public class ${entity}${fileNameSuffix} extends PageLimit {
<#elseif activeRecord>
public class ${entity}${fileNameSuffix} extends Model<${entity}> {
<#else>
public class ${entity}${fileNameSuffix} <#if entitySerialVersionUID>implements Serializable</#if> {
</#if>

<#if entitySerialVersionUID>
  private static final long serialVersionUID = 1L;

</#if>
<#-- 如果包含是删除,添加多个 ID 的字段 -->
<#if fileNameSuffix?contains('Remove')>
  @ApiModelProperty("多个${table.comment!} ID,逗号分隔")
  private String ids;

</#if>
<#-- 临时写法 -->
<#if fileNameSuffix?contains('Get') || fileNameSuffix?contains('Update')>
  @ApiModelProperty("主键")
  @Min(value = 1, message = "${table.comment!} ID 错误")
  private Long id;

</#if>
<#-- ----------  BEGIN 字段循环遍历  -------- -->
<#list table.fields as field>
  <#-- 排除公共字段 -->
  <#if (field.propertyName!='id' && fileNameSuffix?contains('Get')) || (field.propertyName!='delFlag'&&field.propertyName!='createdTime'&&field.propertyName!='createdBy'&&field.propertyName!='updatedTime'&&field.propertyName!='updatedBy'&&field.propertyName!='remark')>
  <#if field.keyFlag>
    <#assign keyPropertyName="${field.propertyName}"/>
  </#if>
  <#if field.comment!?length gt 0>
    <#if swagger??>
  @ApiModelProperty("${field.comment}")
      <#-- 校验 -->
      <#if field.propertyType == 'Long'>
  @NotNull(message = "${field.comment}不能为空")
  @Min(value = 1, message = "${field.comment} 错误")
      <#elseif field.propertyType == 'String'>
  @NotBlank(message = "${field.comment}不能为空")
      <#else>
      </#if>
    <#else>
  /**
   * ${field.comment}
   */
      <#-- 校验 -->
      <#if field.propertyType == 'Long'>
  @NotNull(message = "${field.comment}不能为空")
  @Min(value = 1, message = "${field.comment} 错误")
      <#elseif field.propertyType == 'String'>
  @NotBlank(message = "${field.comment}不能为空")
      <#else>
      </#if>
    </#if>
  </#if>
  <#if field.keyFlag>
    <#-- 主键 -->
    <#if field.keyIdentityFlag>
  @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
    <#elseif idType??>
  @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
    <#elseif field.convert>
  @TableId("${field.annotationColumnName}")
    </#if>
    <#-- 普通字段 -->
  <#elseif field.fill??>
  <#-- -----   存在字段填充设置   ----->
    <#if field.convert>
  @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
    <#else>
  @TableField(fill = FieldFill.${field.fill})
    </#if>
  <#elseif field.convert>
  @TableField("${field.annotationColumnName}")
  </#if>
  <#-- 乐观锁注解 -->
  <#if field.versionField>
  @Version
  </#if>
  <#-- 逻辑删除注解 -->
  <#if field.logicDeleteField>
  @TableLogic
  </#if>
  private ${field.propertyType} ${field.propertyName};

  </#if>
</#list>
<#-- ----------  END 字段循环遍历  -------- -->
<#-- Lombok 模式 -->
<#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
  protected 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>
}

entityVO.java.ftl,生成后需自己调整字段

package ${package.Entity}.vo;

<#if fileNameSuffix?contains('ListVO') || fileNameSuffix?contains('GetVO')>
import com.irms.domain.vo.BaseVO;
</#if>
<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger??>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
  <#if chainModel>
import lombok.experimental.Accessors;
  </#if>
</#if>

/**
 * ${table.comment!}<#if fileNameSuffix?contains('List')>列表展示对象<#elseif fileNameSuffix?contains('Get')>详情展示对象<#else></#if>
 *
<#if author != "">
 * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
<#if table.convert>
  @TableName("${table.name}")
</#if>
<#if entityLombokModel>
  <#if fileNameSuffix?contains('ListVO') || fileNameSuffix?contains('GetVO')>
@EqualsAndHashCode(callSuper = true)
  <#else>
@EqualsAndHashCode(callSuper = false)
  </#if>
  <#if chainModel>
@Accessors(chain = true)
  </#if>
@Data
</#if>
<#if swagger??>
@ApiModel(value = "${entity} <#if fileNameSuffix?contains('List')>列表展示对象<#elseif fileNameSuffix?contains('Get')>详情展示对象<#else></#if>", description = "${table.comment!}<#if fileNameSuffix?contains('List')>列表展示对象<#elseif fileNameSuffix?contains('Get')>详情展示对象<#else></#if>")
</#if>
<#if fileNameSuffix?contains("ListVO") || fileNameSuffix?contains('GetVO')>
public class ${entity + fileNameSuffix} extends BaseVO {
<#elseif activeRecord>
public class ${entity + fileNameSuffix} extends Model<${entity}> {
<#else>
public class ${entity + fileNameSuffix} <#if entitySerialVersionUID>implements Serializable</#if> {
</#if>

<#if entitySerialVersionUID>
  private static final long serialVersionUID = 1L;

</#if>
<#-- ----------  BEGIN 字段循环遍历  -------- -->
<#list table.fields as field>
  <#-- 排除公共字段 -->
  <#if field.propertyName != 'id' && field.propertyName != 'delFlag' && field.propertyName != 'createdTime' && field.propertyName != 'createdBy' && field.propertyName != 'updatedTime' && field.propertyName != 'updatedBy' && field.propertyName != 'remark'>
  <#if field.keyFlag>
    <#assign keyPropertyName="${field.propertyName}"/>
  </#if>
  <#if field.comment!?length gt 0>
    <#if swagger??>
  @ApiModelProperty("${field.comment}")
    <#else>
  /**
   * ${field.comment}
   */
    </#if>
  </#if>
  <#if field.keyFlag>
    <#-- 主键 -->
    <#if field.keyIdentityFlag>
  @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
    <#elseif idType??>
  @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
    <#elseif field.convert>
  @TableId("${field.annotationColumnName}")
    </#if>
    <#-- 普通字段 -->
  <#elseif field.fill??>
  <#-- -----   存在字段填充设置   ----->
    <#if field.convert>
  @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
    <#else>
  @TableField(fill = FieldFill.${field.fill})
    </#if>
  <#elseif field.convert>
  @TableField("${field.annotationColumnName}")
  </#if>
  <#-- 乐观锁注解 -->
  <#if field.versionField>
  @Version
  </#if>
  <#-- 逻辑删除注解 -->
  <#if field.logicDeleteField>
  @TableLogic
  </#if>
  private ${field.propertyType} ${field.propertyName};

  </#if>
</#list>
<#-- ----------  END 字段循环遍历  -------- -->
<#-- Lombok 模式 -->
<#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
  protected 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>
}

mapper.java.ftl

package ${package.Mapper};

import ${package.Entity}.${entity};
import ${superMapperClassPackage};

/**
 * ${table.comment!} Mapper
 *
<#if author != "">
 * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {

}
</#if>

service.java

package ${package.Service};

import ${package.Entity}.${entity};
import ${superServiceClassPackage};

/**
 * ${table.comment!} 服务
 *
<#if author != "">
 * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
<#if kotlin>
interface ${table.serviceName} : ${superServiceClass}<${entity}>
<#else>
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {

}
</#if>

serviceImpl.java.ftl

package ${package.ServiceImpl};

import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * ${table.comment!} 服务实现
 *
<#if author != "">
  * @author ${author}
</#if>
<#if date != "">
 * @since ${date}
</#if>
 */
@Service
<#if kotlin>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {

}
<#else>
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {

  @Autowired
  private ${table.mapperName} ${table.mapperName?substring(0, 1)?lower_case}${table.mapperName?substring(1)};

}
</#if>

struct.java.ftl

package ${package.Parent};

import ${package.Entity}.${entity};
import ${package.Entity}.qo.*;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface ${entity}Struct {

  ${entity}Struct INSTANCE = Mappers.getMapper(${entity}Struct.class);

  ${entity} to(${entity}SaveQO obj);

  ${entity} to(${entity}UpdateQO obj);
}

;