2.使用的版本
Mybatis-plus 3.3.1
Mybatis-plus-generator 3.3.1
Freemarker 2.3.30
3. pom依赖
<!-- 2、MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1</version>
</dependency>
<!--使用freemark模板引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
4. 生成器代码
package com.example.open.generator;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
* Date: 2020/5/20
* Time: 9:13
*
* @author wcb
* Description: mybatis plus 代码生成器
*/
public class MybatisPlusGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
System.out.println(("请输入" + tip + ":"));
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
//定义输出的目录
gc.setOutputDir(projectPath + "/open-platform-portal-backend" + "/src/main/java");
gc.setAuthor("wcb");
//是否打开输出的目录
gc.setOpen(false);
//设置生成实体日期属性的类型,我这里用的是util中的Date
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setDbType(DbType.ORACLE);
dsc.setUrl("jdbc:oracle:thin:@localhost:1521:open");
dsc.setDriverName("oracle.jdbc.driver.OracleDriver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.open");
pc.setController("web.controller");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
mpg.setPackageInfo(pc);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
//指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
templateConfig.setEntity("templates/entityEngineTemplate/entity.java");
templateConfig.setController("templates/entityEngineTemplate/controller.java");
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//实体共同继承的基础类, 里面抽取了共同的一些属性
strategy.setSuperEntityClass("com.example.open.entity.BaseEntity");
// 写于父类中的公共字段,一下为生成的实体所忽略的字段
strategy.setSuperEntityColumns("ID", "CREATE_TIME", "MODIFY_TIME", "DELETED");
//逻辑删除属性名称
strategy.setLogicDeleteFieldName("DELETED");
//是否使用lombok注解
strategy.setEntityLombokModel(true);
//是否定义输出的实体类有builder模式的调用链
strategy.setEntityBuilderModel(true);
//是否生成字段注解:读取表中的注释
strategy.setEntityTableFieldAnnotationEnable(true);
//是否生成@RestController注解的controller
strategy.setRestControllerStyle(true);
// controller共同继承的公共父类
strategy.setSuperControllerClass("com.example.open.web.controller.BaseController");
//驼峰转连接字符- , 例如PlatformUser --> platform-user
//strategy.setControllerMappingHyphenStyle(true);
//去除表名的前缀的固定生成实体
strategy.setTablePrefix("tb_");
//决定是否使用自定义的实体类命名策略,注意使用自定义命名实体的方式时,请不要批量处理
String choose = scanner("是否使用自定义的实体类名称(y/n)?");
if (StringUtils.matches(choose, "y")) {
strategy.setInclude(scanner("请输入表名"));
strategy.setNameConvert(new CustomNameConvert(strategy));
//自定义注入属性,在使用单个生成时,可以使用
InjectionConfig injectionConfig = new InjectionConfig() {
//自定义属性注入:abc
//在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
@Override
public void initMap() {
List<TableInfo> list = this.getConfig().getTableInfoList();
if (list != null && !list.isEmpty()) {
TableInfo info = list.get(0);
Map<String, Object> map = new HashMap<>();
map.put("serviceImplNamePath", info.getEntityPath() + "Service");
super.setMap(map);
}
}
};
mpg.setCfg(injectionConfig);
} else {
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
}
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
5.实现的NameConvert
/**
* Created with IntelliJ IDEA.
* Date: 2020/5/20
* Time: 10:45
*
* @author wcb
* Description: 自定义的表与实体转换策略
*/
public class CustomNameConvert implements INameConvert {
/**
* 策略配置
*/
private StrategyConfig strategyConfig;
public CustomNameConvert(StrategyConfig strategyConfig) {
this.strategyConfig = strategyConfig;
}
@Override
public String entityNameConvert(TableInfo tableInfo) {
return MybatisPlusGenerator.scanner("请输入实体名称,注意驼峰命名");
}
@Override
public String propertyNameConvert(TableField field) {
return processName(field.getName(), strategyConfig.getNaming());
}
/**
* 处理字段名称
*
* @return 根据策略返回处理后的名称
*/
private String processName(String name, NamingStrategy strategy) {
return processName(name, strategy, strategyConfig.getFieldPrefix());
}
/**
* 处理表/字段名称
*
* @param name ignore
* @param strategy ignore
* @param prefix ignore
* @return 根据策略返回处理后的名称
*/
private String processName(String name, NamingStrategy strategy, String[] prefix) {
boolean removePrefix = false;
if (prefix != null && prefix.length != 0) {
removePrefix = true;
}
String propertyName;
if (removePrefix) {
if (strategy == NamingStrategy.underline_to_camel) {
// 删除前缀、下划线转驼峰
propertyName = NamingStrategy.removePrefixAndCamel(name, prefix);
} else {
// 删除前缀
propertyName = NamingStrategy.removePrefix(name, prefix);
}
} else if (strategy == NamingStrategy.underline_to_camel) {
// 下划线转驼峰
propertyName = NamingStrategy.underlineToCamel(name);
} else {
// 不处理
propertyName = name;
}
return propertyName;
}
}
6. Entity.java.ftl(注意:需要放在resources目录下,需要在templateConfig中指定路径,ps:不要格式化模板代码,不然产生的代码缩进会变化)
package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
import com.baomidou.mybatisplus.annotation.TableField;
</#list>
<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
</#if>
/**
* <p>
* ${table.comment!}
* </p>
*
* @author ${author}
* @since ${date}
*/
<#if entityLombokModel>
@Data
<#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
</#if>
@Accessors(chain = true)
</#if>
<#if table.convert>
@TableName("${table.name}")
</#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} implements Serializable {
</#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 swagger2>
@ApiModelProperty(value = "${field.comment}")
<#else>
/**
* ${field.comment}
*/
</#if>
@TableField("${field.name}")
</#if>
<#if field.keyFlag>
<#-- 主键 -->
<#if field.keyIdentityFlag>
@TableId(value = "${field.name}", type = IdType.AUTO)
<#elseif idType??>
@TableId(value = "${field.name}", type = IdType.${idType})
<#elseif field.convert>
@TableId("${field.name}")
</#if>
<#-- 普通字段 -->
<#elseif field.fill??>
<#-- ----- 存在字段填充设置 ----->
<#if field.convert>
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
<#else>
@TableField(fill = FieldFill.${field.fill})
</#if>
<#elseif field.convert>
@TableField("${field.name}")
</#if>
<#-- 乐观锁注解 -->
<#if (versionFieldName!"") == field.name>
@Version
</#if>
<#-- 逻辑删除注解 -->
<#if (logicDeleteFieldName!"") == field.name>
@TableLogic
</#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 entityBuilderModel>
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 entityBuilderModel>
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>
}
7. controller.java.ftl
package ${package.Controller};
import org.springframework.web.bind.annotation.RequestMapping;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ${package.Entity}.${entity};
import ${package.Service}.${table.serviceName};
import com.zjca.open.util.ResultWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>
/**
* <p>
* ${table.comment!} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#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} ${cfg.serviceImplNamePath};
/**
* 查询封装条件的结果集
*
* @param entity 查询参数封装对象,一般为实体vo,若是需要查询全部,条件都为空即可
* @return 返回查询结果集与组拼的响应对象
*
*/
@GetMapping("list")
<#if !restControllerStyle>
@ResponseBody
</#if>
public Object list(@RequestBody ${entity} entity) {
return new ResultWrapper<>(${cfg.serviceImplNamePath}.list(new QueryWrapper<>(entity)));
}
/**
* 返回一个${table.comment}对象
*
* @param id 需要查询的对象ID
* @return 返回ID匹配的一条数据与组拼的响应对象
*/
<#if !restControllerStyle>
@ResponseBody
</#if>
@GetMapping(value = "getOne/{id}", produces = "application/json;charset=utf-8")
public Object get(@PathVariable String id) {
return new ResultWrapper<>(${cfg.serviceImplNamePath}.getById(id));
}
/**
* 新增${table.comment}对象
*
* @param entity 查询参数封装对象,一般为实体vo,若是需要查询全部,条件都为空即可
* @return 返回插入结果(true或false)与组拼的响应对象
*/
@PostMapping("insert")
<#if !restControllerStyle>
@ResponseBody
</#if>
public Object insert(@RequestBody ${entity} entity) {
return new ResultWrapper<>(${cfg.serviceImplNamePath}.save(entity));
}
/**
* 更新一个${table.comment}对象
*
* @param entity 需要更新的${table.comment}对象
* @return 返回操作结果(true或false)与组拼的响应对象
*/
<#if !restControllerStyle>
@ResponseBody
</#if>
@PatchMapping(value = "update", produces = "application/json;charset=utf-8", consumes = "application/json;charset=utf-8")
public Object update(@RequestBody ${entity} entity) {
return new ResultWrapper<>(${cfg.serviceImplNamePath}.update(new QueryWrapper<>(entity)));
}
/**
* 删除一个${table.comment}对象
*
* @param id 需要对象的Id
* @return 返回操作结果(true或false)与组拼的响应对象
*/
<#if !restControllerStyle>
@ResponseBody
</#if>
@DeleteMapping(value = "delete/{id}", produces = "application/json;charset=utf-8")
public Object delete(@PathVariable String id) {
return new ResultWrapper<>(${cfg.serviceImplNamePath}.removeById(id));
}
}
</#if>
8. 如果还有疑惑,请多看配置类
/*
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.baomidou.mybatisplus.generator.config;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.config.po.LikeTable;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
/**
* 策略配置项
*
* @author YangHu, tangguo, hubin
* @since 2016/8/30
*/
@Data
@Accessors(chain = true)
public class StrategyConfig {
/**
* 是否大写命名
*/
private boolean isCapitalMode = false;
/**
* 是否跳过视图
*/
private boolean skipView = false;
/**
* 名称转换
*/
private INameConvert nameConvert;
/**
* 数据库表映射到实体的命名策略
*/
private NamingStrategy naming = NamingStrategy.no_change;
/**
* 数据库表字段映射到实体的命名策略
* <p>未指定按照 naming 执行</p>
*/
private NamingStrategy columnNaming = null;
/**
* 表前缀
*/
@Setter(AccessLevel.NONE)
private String[] tablePrefix;
/**
* 字段前缀
*/
@Setter(AccessLevel.NONE)
private String[] fieldPrefix;
/**
* 自定义继承的Entity类全称,带包名
*/
@Setter(AccessLevel.NONE)
private String superEntityClass;
/**
* 自定义基础的Entity类,公共字段
*/
@Setter(AccessLevel.NONE)
private String[] superEntityColumns;
/**
* 自定义继承的Mapper类全称,带包名
*/
private String superMapperClass = ConstVal.SUPER_MAPPER_CLASS;
/**
* 自定义继承的Service类全称,带包名
*/
private String superServiceClass = ConstVal.SUPER_SERVICE_CLASS;
/**
* 自定义继承的ServiceImpl类全称,带包名
*/
private String superServiceImplClass = ConstVal.SUPER_SERVICE_IMPL_CLASS;
/**
* 自定义继承的Controller类全称,带包名
*/
private String superControllerClass;
/**
* 需要包含的表名(与exclude二选一配置)
* @since 3.3.0 正则匹配不再支持,请使用{@link #setLikeTable(LikeTable)}}
*/
@Setter(AccessLevel.NONE)
private String[] include = null;
/**
* 需要排除的表名
* @since 3.3.0 正则匹配不再支持,请使用{@link #setNotLikeTable(LikeTable)}}
*/
@Setter(AccessLevel.NONE)
private String[] exclude = null;
/**
* 实体是否生成 serialVersionUID
*/
private boolean entitySerialVersionUID = true;
/**
* 【实体】是否生成字段常量(默认 false)<br>
* -----------------------------------<br>
* public static final String ID = "test_id";
*/
private boolean entityColumnConstant = false;
/**
* 【实体】是否为构建者模型(默认 false)<br>
* -----------------------------------<br>
* public User setName(String name) { this.name = name; return this; }
*/
private boolean entityBuilderModel = false;
/**
* 【实体】是否为lombok模型(默认 false)<br>
* <a href="https://projectlombok.org/">document</a>
*/
private boolean entityLombokModel = false;
/**
* Boolean类型字段是否移除is前缀(默认 false)<br>
* 比如 : 数据库字段名称 : 'is_xxx',类型为 : tinyint. 在映射实体的时候则会去掉is,在实体类中映射最终结果为 xxx
*/
private boolean entityBooleanColumnRemoveIsPrefix = false;
/**
* 生成 <code>@RestController</code> 控制器
* <pre>
* <code>@Controller</code> -> <code>@RestController</code>
* </pre>
*/
private boolean restControllerStyle = false;
/**
* 驼峰转连字符
* <pre>
* <code>@RequestMapping("/managerUserActionHistory")</code> -> <code>@RequestMapping("/manager-user-action-history")</code>
* </pre>
*/
private boolean controllerMappingHyphenStyle = false;
/**
* 是否生成实体时,生成字段注解
*/
private boolean entityTableFieldAnnotationEnable = false;
/**
* 乐观锁属性名称
*/
private String versionFieldName;
/**
* 逻辑删除属性名称
*/
private String logicDeleteFieldName;
/**
* 表填充字段
*/
private List<TableFill> tableFillList = null;
/**
* 启用sql过滤
* 语法不能支持使用sql过滤表的话,可以考虑关闭此开关.
* 目前所知微软系需要关闭,其他数据库等待反馈,sql可能要改动一下才能支持,没数据库环境搞,请手动关闭使用内存过滤的方式。
*
* @since 3.3.1
*/
private boolean enableSqlFilter = true;
/**
* 包含表名
*
* @since 3.3.0
*/
private LikeTable likeTable;
/**
* 不包含表名
*
* @since 3.3.0
*/
private LikeTable notLikeTable;
/**
* 大写命名、字段符合大写字母数字下划线命名
*
* @param word 待判断字符串
*/
public boolean isCapitalModeNaming(String word) {
return isCapitalMode && StringUtils.isCapitalMode(word);
}
/**
* 表名称包含指定前缀
*
* @param tableName 表名称
*/
public boolean containsTablePrefix(String tableName) {
if (null != tableName) {
String[] tps = getTablePrefix();
if (null != tps) {
return Arrays.stream(tps).anyMatch(tableName::contains);
}
}
return false;
}
public NamingStrategy getColumnNaming() {
if (null == columnNaming) {
// 未指定以 naming 策略为准
return naming;
}
return columnNaming;
}
public StrategyConfig setTablePrefix(String... tablePrefix) {
this.tablePrefix = tablePrefix;
return this;
}
public boolean includeSuperEntityColumns(String fieldName) {
if (null != superEntityColumns) {
// 公共字段判断忽略大小写【 部分数据库大小写不敏感 】
return Arrays.stream(superEntityColumns).anyMatch(e -> e.equalsIgnoreCase(fieldName));
}
return false;
}
public StrategyConfig setSuperEntityColumns(String... superEntityColumns) {
this.superEntityColumns = superEntityColumns;
return this;
}
public StrategyConfig setInclude(String... include) {
this.include = include;
return this;
}
public StrategyConfig setExclude(String... exclude) {
this.exclude = exclude;
return this;
}
public StrategyConfig setFieldPrefix(String... fieldPrefixs) {
this.fieldPrefix = fieldPrefixs;
return this;
}
public StrategyConfig setSuperEntityClass(String superEntityClass) {
this.superEntityClass = superEntityClass;
return this;
}
/**
* <p>
* 设置实体父类,该设置自动识别公共字段<br/>
* 属性 superEntityColumns 改配置无需再次配置
* </p>
* <p>
* 注意!!字段策略要在设置实体父类之前有效
* </p>
*
* @param clazz 实体父类 Class
* @return
*/
public StrategyConfig setSuperEntityClass(Class<?> clazz) {
return setSuperEntityClass(clazz, null);
}
/**
* <p>
* 设置实体父类,该设置自动识别公共字段<br/>
* 属性 superEntityColumns 改配置无需再次配置
* </p>
*
* @param clazz 实体父类 Class
* @param columnNaming 字段命名策略
* @return
*/
public StrategyConfig setSuperEntityClass(Class<?> clazz, NamingStrategy columnNaming) {
if (null != columnNaming) {
this.columnNaming = columnNaming;
}
this.superEntityClass = clazz.getName();
convertSuperEntityColumns(clazz);
return this;
}
public StrategyConfig setSuperControllerClass(Class<?> clazz) {
this.superControllerClass = clazz.getName();
return this;
}
public StrategyConfig setSuperControllerClass(String superControllerClass) {
this.superControllerClass = superControllerClass;
return this;
}
/**
* <p>
* 父类 Class 反射属性转换为公共字段
* </p>
*
* @param clazz 实体父类 Class
*/
protected void convertSuperEntityColumns(Class<?> clazz) {
List<Field> fields = TableInfoHelper.getAllFields(clazz);
this.superEntityColumns = fields.stream().map(field -> {
if (null == columnNaming || columnNaming == NamingStrategy.no_change) {
return field.getName();
}
return StringUtils.camelToUnderline(field.getName());
}).distinct().toArray(String[]::new);
}
/**
* @deprecated please use `setEntityTableFieldAnnotationEnable`
*/
@Deprecated
public StrategyConfig entityTableFieldAnnotationEnable(boolean isEnableAnnotation) {
entityTableFieldAnnotationEnable = isEnableAnnotation;
return this;
}
}