v3.5.3、v3.5.3.1
和 v3.5.2(往下翻)区别不大,主要是FreemarkerTemplateEngine
修改的多,但我还是全部重新贴一遍,我之前的写法中在 Controller 方法里进行了 try catch,但实际上可以用全局异常捕获。
参照阿里巴巴 Java 开发手册,将其中每一层传递参数的入参称为 Query Object,所以我命名为 XxxQO,目前是根据 entity 的属性生成的,生成后需要手动删除不需要的参数,以及更新参数校验注解。返回到页面的响应结果是 VO(View Object)。
另外我现在改用 springdoc-openapi-ui 了,所以有些注解还在用 Swagger 的需要改下。
代码不用全部复制,重点关注 CodeGenerator 和 FreemarkerTemplateEngine 这两个类就行,其他的模板文件可以从源码中拷贝出来再修改。
代码生成器
CodeGenerator.java
package top.zhogjianhao.mybatis;
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.rules.NamingStrategy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import top.zhogjianhao.YamlUtils;
import top.zhogjianhao.base.BaseController;
import top.zhogjianhao.base.model.BaseEntity;
import top.zhogjianhao.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 代码生成器
*/
@Component
public class CodeGenerator {
private static final String DB_URL;
private static final String DB_USERNAME;
private static final String DB_PASSWORD;
private static final String TABLE_PREFIX;
private static final String OUTPUT_DIR = FileUtils.getProjectPath() + "/generator/src/main/java";
static {
Map<String, Object> yamlMap = YamlUtils.load(FileUtils.getProjectPath() + "/admin/src/main/resources/application.yml");
Object urlObj = YamlUtils.get(yamlMap, "spring.datasource.url");
DB_URL = urlObj != null ? urlObj.toString() : "";
Object usernameObj = YamlUtils.get(yamlMap, "spring.datasource.username");
DB_USERNAME = usernameObj != null ? usernameObj.toString() : "";
Object passwordObj = YamlUtils.get(yamlMap, "spring.datasource.password");
DB_PASSWORD = passwordObj != null ? passwordObj.toString() : "";
Object prefixObj = YamlUtils.get(yamlMap, "mybatis-plus.global-config.db-config.table-prefix");
TABLE_PREFIX = prefixObj != null ? prefixObj.toString() : "";
}
/**
* 读取控制台输入内容
*/
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) throws IOException {
// 自定义模板,key 为“自定义的包名:Entity 后缀”,value 为模板路径
Map<String, String> customFile = new HashMap<>(8);
customFile.put("model/qo:GetQO", "/generator/entityQO.java");
customFile.put("model/qo:PageQO", "/generator/entityQO.java");
customFile.put("model/qo:SaveQO", "/generator/entityQO.java");
customFile.put("model/qo:UpdateQO", "/generator/entityQO.java");
customFile.put("model/qo:RemoveQO", "/generator/entityQO.java");
customFile.put("model/vo:PageVO", "/generator/entityVO.java");
customFile.put("model/vo:GetVO", "/generator/entityVO.java");
customFile.put("struct:Struct", "/generator/struct.java");
// 删除之前生成的文件
FileUtils.deleteDirectory(new File(OUTPUT_DIR));
// 代码生成器
new AutoGenerator(configBuilder(new DataSourceConfig.Builder(DB_URL, DB_USERNAME, DB_PASSWORD)))
// 全局配置
.global(configBuilder(new GlobalConfig.Builder()
// 禁用打开生成目录
.disableOpenDir()
// 输出目录,默认 windows: D:// linux or mac: /tmp
.outputDir(OUTPUT_DIR)
// 作者,默认无
.author("ZhongJianhao")
// 注释时间(@since),默认 yyyy-MM-dd
.commentDate("")
// 开启 swagger 模式,默认 false
.enableSwagger()
))
// 包配置
.packageInfo(configBuilder(new PackageConfig.Builder()
// 模块名
.moduleName("")
// 实体包名
.entity("model.entity")
// 父包名
.parent("top.zhogjianhao")
))
// 自定义配置
.injection(configBuilder(new InjectionConfig.Builder()
.beforeOutputFile((tableInfo, stringObjectMap) -> {
// 不启用 @TableName 注解
// tableInfo.setConvert(false);
// 自定义 Mapper XML 生成目录
ConfigBuilder config = (ConfigBuilder) stringObjectMap.get("config");
Map<OutputFile, String> pathInfoMap = config.getPathInfo();
pathInfoMap.put(OutputFile.xml, pathInfoMap.get(OutputFile.xml).replaceAll("/java.*", "/resources/mapper"));
stringObjectMap.put("config", config);
})
// 自定义文件,比如 VO
.customFile(customFile)
))
// 策略配置
.strategy(configBuilder(new StrategyConfig.Builder()
// 表名
.addInclude(scannerNext("请输入表名(英文逗号分隔):").split(","))
// 过滤表前缀
.addTablePrefix(TABLE_PREFIX)
// Entity 策略
.entityBuilder()
// 开启 Lombok 模式
.enableLombok()
// 禁用生成 serialVersionUID
.disableSerialVersionUID()
// 数据库表映射到实体的命名策略:下划线转驼峰
.naming(NamingStrategy.underline_to_camel)
// 主键策略为自增,默认 IdType.AUTO
.idType(IdType.INPUT)
// 父类
.superClass(BaseEntity.class)
// 覆盖已有文件
.enableFileOverride()
// Controller 策略
.controllerBuilder()
// 生成 @RestController 注解
.enableRestStyle()
// 父类
.superClass(BaseController.class)
.enableFileOverride()
// Service 策略
.serviceBuilder()
.enableFileOverride()
// Mapper 策略
.mapperBuilder()
.enableFileOverride()
))
// 模板配置
.template(configBuilder(new TemplateConfig.Builder()
// 自定义模板:https://github.com/baomidou/generator/tree/develop/mybatis-plus-generator/src/main/resources/templates
.entity("/generator/entity.java")
.mapper("/generator/mapper.java")
.service("/generator/service.java")
.serviceImpl("/generator/serviceImpl.java")
.controller("/generator/controller.java")
))
// 执行并指定模板引擎
.execute(new FreemarkerTemplateEngine());
}
}
自定义模板引擎(FreeMarker)
FreemarkerTemplateEngine.java
package top.zhogjianhao.mybatis;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import top.zhogjianhao.StringUtils;
import java.io.File;
import java.util.List;
import java.util.Map;
/**
* 自定义模板引擎处理,用于生成 QO、VO 等
*/
public class FreemarkerTemplateEngine extends com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine {
@Override
protected void outputCustomFile(List<CustomFile> customFiles, TableInfo tableInfo, Map<String, Object> objectMap) {
String entityName = tableInfo.getEntityName();
String parentPath = this.getPathInfo(OutputFile.parent);
customFiles.forEach((file) -> {
String filePath = StringUtils.isNotBlank(file.getFilePath()) ? file.getFilePath() : parentPath;
if (StringUtils.isNotBlank(file.getPackageName())) {
filePath = filePath + File.separator + file.getPackageName();
filePath = filePath.replaceAll("\\.", "\\" + File.separator);
}
// 自定义的包名:Entity 后缀:model/qo:GetQO
String[] customFileNames = file.getFileName().split(":");
String fileName = filePath + (!customFileNames[0].startsWith("/") ? File.separator : "") + customFileNames[0] + File.separator + entityName + customFileNames[1] + ".java";
String templatePath = file.getTemplatePath();
if (!templatePath.endsWith(".ftl")) {
templatePath += ".ftl";
}
// 在路由名后加 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());
}
// 新增文件名后缀(v3.5.1 还有的)
objectMap.put("fileNameSuffix", customFileNames[1]);
this.outputFile(new File(fileName), objectMap, templatePath, file.isFileOverride());
});
}
}
自定义模板 RESTful(FreeMarker)
src/main/resources/template/generator
controller.java.ftl
package ${package.Controller};
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import ${package.Parent}.base.model.PageVO;
import ${package.Parent}.base.model.R;
import ${package.Entity}.${entity};
import ${package.Entity?substring(0, package.Entity?last_index_of("."))}.qo.*;
import ${package.Entity?substring(0, package.Entity?last_index_of("."))}.vo.*;
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>
import ${package.Service}.${table.serviceName};
import ${package.Parent}.struct.${entity}Struct;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
* ${table.comment!} 控制器
*
<#if author != "">
* @author ${author}
</#if>
<#if date != "">
* @since ${date}
</#if>
*/
@Tag(name = "${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)};
@Operation(summary = "${table.comment!}列表")
@GetMapping
public R<List<${entity}>> list(@Validated ${entity}PageQO query) {
LambdaQueryWrapper<${entity}> queryWrapper = new LambdaQueryWrapper<${entity}>().orderByDesc(${entity}::getId);
if (query.getCurrent() == 0) {
return ok(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.list(queryWrapper));
}
return ok(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.page(query, queryWrapper));
}
@Operation(summary = "${table.comment!}详情")
@GetMapping("/{id}")
public R<${entity}GetVO> get(@Validated ${entity}GetQO query) {
return ok(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.getById(query.getId()));
}
@Operation(summary = "保存${table.comment!}")
@Transactional(rollbackFor = {Exception.class, RuntimeException.class})
@PostMapping
public R save(@RequestBody @Validated ${entity}SaveQO obj) {
return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.save(${entity}Struct.INSTANCE.to(obj)), SAVE_FAILED_MSG);
}
@Operation(summary = "更新${table.comment!}")
@Transactional(rollbackFor = {Exception.class, RuntimeException.class})
@PutMapping("/{id}")
public R update(@RequestBody @Validated ${entity}UpdateQO obj) {
return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.updateById(${entity}Struct.INSTANCE.to(obj)), UPDATE_FAILED_MSG);
}
@Operation(summary = "删除${table.comment!}")
@Transactional(rollbackFor = {Exception.class, RuntimeException.class})
@DeleteMapping("/{ids}")
public R remove(@Validated ${entity}RemoveQO query) {
return custom(${table.serviceName?substring(1, 2)?lower_case}${table.serviceName?substring(2)}.removeByIds(Arrays.asList(query.getIds().split(","))), REMOVE_FAILED_MSG);
}
}
</#if>
service.java.ftl
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>
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>
entity.java.ftl
package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger??>
import io.swagger.v3.oas.annotations.media.Schema;
</#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 swagger??>
@Schema(title = "${table.comment!}")
</#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 superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#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 != 'tenantId' && field.propertyName != 'deleted' && field.propertyName != 'createdTime' && field.propertyName != 'createdBy' && field.propertyName != 'updatedTime' && field.propertyName != 'updatedBy' && field.propertyName != 'remark'>
<#if field.keyFlag>
<#assign keyPropertyName="${field.propertyName}"/>
</#if>
/**
* ${field.comment}
*/
<#if field.comment!?length gt 0 && swagger??>
@Schema(title = "${field.comment}")
</#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('PageQO')>
import top.zhogjianhao.base.model.BasePageQO;
<#elseif fileNameSuffix?contains('GetQO')>
import top.zhogjianhao.base.model.BaseGetQO;
</#if>
import ${package.Entity}.${entity};
<#--<#list table.importPackages as pkg>
import ${pkg};
</#list>-->
<#if swagger??>
import io.swagger.v3.oas.annotations.media.Schema;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
<#if chainModel>
import lombok.experimental.Accessors;
</#if>
</#if>
import javax.validation.constraints.*;
import java.time.LocalDateTime;
/**
* ${table.comment!}<#if fileNameSuffix?contains('Page')>分页<#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 swagger??>
@Schema(title = "${table.comment!} <#if fileNameSuffix?contains('Page')>分页入参<#elseif fileNameSuffix?contains('Get')>详情入参<#elseif fileNameSuffix?contains('Save')>保存入参<#elseif fileNameSuffix?contains('Update')>更新入参<#elseif fileNameSuffix?contains('Remove')>删除入参<#else></#if>")
</#if>
<#if entityLombokModel>
<#if fileNameSuffix?contains('PageQO')>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
</#if>
<#if chainModel>
@Accessors(chain = true)
</#if>
@Data
</#if>
<#if fileNameSuffix?contains("PageQO")>
public class ${entity}${fileNameSuffix} extends BasePageQO<${entity}> {
<#elseif fileNameSuffix?contains("GetQO")>
public class ${entity}${fileNameSuffix} extends BaseGetQO {
<#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')>
@Schema(title = "多个${table.comment!} ID,逗号分隔")
private String ids;
</#if>
<#-- 临时写法 -->
<#if fileNameSuffix?contains('Get') || fileNameSuffix?contains('Update')>
@Schema(title = "ID")
@Min(value = 1, message = "${table.comment!} ID 错误")
private Long id;
</#if>
<#-- ---------- BEGIN 字段循环遍历 -------- -->
<#list table.fields as field>
<#-- 排除公共字段 -->
<#if (fileNameSuffix?contains('Get') && field.propertyName=='id') || (fileNameSuffix?contains('Update') && field.propertyName!='createdTime') || (field.propertyName!='tenantId'&&field.propertyName!='createdBy'&&field.propertyName!='updatedBy'&&field.propertyName!='updatedTime')>
<#if field.keyFlag>
<#assign keyPropertyName="${field.propertyName}"/>
</#if>
<#if field.comment!?length gt 0 && swagger??>
@Schema(title = "${field.comment}")
</#if>
<#-- 校验 -->
<#if field.propertyType == 'Long'>
@NotNull(message = "${field.comment}不能为空")
@Min(value = 1, message = "${field.comment} 错误")
<#elseif field.propertyType == 'String'>
@NotBlank(message = "${field.comment}不能为空")
<#elseif field.propertyType == 'LocalDateTime'>
@NotNull(message = "${field.comment}不能为空")
<#else>
</#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('PageVO')>
import top.zhogjianhao.base.model.PageVO;
<#elseif fileNameSuffix?contains('GetVO')>
import top.zhogjianhao.base.model.BaseEntity;
</#if>
<#--<#list table.importPackages as pkg>
import ${pkg};
</#list>-->
<#if swagger??>
import io.swagger.v3.oas.annotations.media.Schema;
</#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 swagger??>
@Schema(title = "${table.comment!} <#if fileNameSuffix?contains('Page')>分页响应<#elseif fileNameSuffix?contains('Get')>详情响应<#else></#if>")
</#if>
<#if entityLombokModel>
<#if fileNameSuffix?contains('PageVO') || fileNameSuffix?contains('GetVO')>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
</#if>
<#if chainModel>
@Accessors(chain = true)
</#if>
@Data
</#if>
<#if fileNameSuffix?contains("PageVO")>
public class ${entity + fileNameSuffix} extends PageVO {
<#elseif fileNameSuffix?contains('GetVO')>
public class ${entity + fileNameSuffix} extends BaseEntity {
<#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 != 'tenantId' && field.propertyName != 'deleted' && 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 && swagger??>
@Schema(title = "${field.comment}")
</#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>
}
struct.java.ftl
package ${package.Parent};
import ${package.Entity}.${entity};
import ${package.Entity?substring(0, package.Entity?last_index_of("."))}.qo.*;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ${entity}Struct {
${entity}Struct INSTANCE = Mappers.getMapper(${entity}Struct.class);
${entity} to(${entity}SaveQO obj);
${entity} to(${entity}UpdateQO obj);
}
生成后的文件用到的自建类
分页响应
PageVO.java
package top.zhogjianhao.base.model;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(title = "分页响应")
@Data
public class PageVO<T> implements Serializable {
@Schema(title = "分页数据")
private List<T> records;
// @Schema(title = "当前页码")
// private long current;
// @Schema(title = "每页条数")
// private long size;
// @Schema(title = "总条数")
// private long total;
public PageVO() {
}
public PageVO(Page<T> page) {
this.records = page.getRecords();
// this.total = page.getTotal();
// this.size = page.getSize();
// this.current = page.getCurrent();
}
}
响应结果
R.java
package top.zhogjianhao.base.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(title = "响应结果")
@Data
public class R<T> {
@Schema(title = "编码")
private int code;
@Schema(title = "消息")
private String msg;
@Schema(title = "数据")
private T data;
@Schema(title = "当前页码")
private Long current;
@Schema(title = "每页条数")
private Long size;
@Schema(title = "总条数")
private Long total;
public R() {
}
public R(int code) {
this.code = code;
}
public R(int code, String msg) {
this.code = code;
this.msg = msg;
}
public R(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
分页入参
BasePageQO
package top.zhogjianhao.base.model;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.List;
/**
* 分页入参,用于隐藏非分页参数
*
* @param <T> 实体类型
* @author ZhongJianhao
*/
public class BasePageQO<T> extends Page<T> implements Serializable {
@Schema(hidden = true)
protected List<T> records;
@Schema(hidden = true)
protected long total;
@Schema(title = "每页条数")
protected long size;
@Schema(title = "当前页码")
protected Long current;
@Schema(hidden = true)
protected List<OrderItem> orders;
@Schema(hidden = true)
protected boolean optimizeCountSql;
@Schema(hidden = true)
protected boolean searchCount;
@Schema(hidden = true)
protected boolean optimizeJoinOfCountSql;
@Schema(hidden = true)
protected String countId;
@Schema(hidden = true)
protected Long maxLimit;
@Schema(hidden = true)
protected Long pages;
@Override
public long getCurrent() {
// 不传 current 参数时,默认为 0,为 0 时忽略分页相关出参
if (current == null) {
return 0;
}
return current;
}
@Override
public Page<T> setCurrent(long current) {
this.current = current;
return this;
}
@Override
public long getSize() {
return size;
}
@Override
public Page<T> setSize(long size) {
this.size = size;
return this;
}
}
详情入参
BaseGetQO.java
package top.zhogjianhao.base.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.Min;
import java.io.Serializable;
@Data
public class BaseGetQO implements Serializable {
@Schema(title = "ID")
@Min(value = 1, message = "ID 错误")
private Long id;
}
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);
}
评论区