在开发的过程中,有很多重复的crud的操作,对于单表的增删改查除了表名不一样,整个逻辑其实都是大同小异的,虽然说增删改查技术上没有什么难度,但是总是要人去写,所以我之前每次写这部分代码的时候就感觉不得劲呀,让人感觉到很是无聊,便搜了搜,有没有什么办法可以一键生成单表的增删改查的,还真有,于是尝试了下,感觉还不错。为了解放我们的双手,减少重复性代码的编写,我推荐使用插件:mybatis-plus-generator 进行代码自动生成单表的简单操作。
下面我将详细介绍通过 mybatis-plus-generator 插件自动生成 controller、service、mapper、serviceImpl相关代码,以及如果你对前端所有兴趣,这个插件还可以做到一键生成对应前端的分页查询、新增、删除、修改等功能。
官方文档:https://baomidou.com/pages/981406/
<!-- 生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mapStruct开始 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!--mapstruct编译-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- web包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--velocity模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- freemarker模板 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.4.2</version>
</dependency>
<!-- mybatis-plus -->
/**
* 数据库地址
*/
private static final String DATA_SOURCE = "jdbc:mysql://xxx.xxx.xxx.xxx:3306/你的数据库?userUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8";
/**
* 用户名
*/
private static final String USERNAME = "用户名";
/**
* 密码
*/
private static final String PASSWORD = "密码";
/**
* 功能描述:
* 〈 数据库配置 〉
*
* @param map 枚举值
* @return : com.baomidou.mybatisplus.generator.config.DataSourceConfig.Builder
* @throws
* @author : yls
* @date : 2023/4/3 9:58
*/
private static DataSourceConfig.Builder getDataSourceBuilder(Map<String, IColumnType> map) {
// 数据源配置
return new DataSourceConfig
.Builder(
DATA_SOURCE,
USERNAME,
PASSWORD)
.dbQuery(new MySqlQuery())
.keyWordsHandler(new MySqlKeyWordsHandler() {
@Override
public boolean isKeyWords(String columnName) {
//获取当前正在生成的字段名称
fieldName = columnName;
return this.getKeyWords().contains(columnName.toUpperCase(Locale.ENGLISH));
}
})
.typeConvert(new MySqlTypeConvert() {
@Override
public IColumnType processTypeConvert(GlobalConfig globalConfig,
String fieldType) {
//根据字段名称判断是否需要转换为枚举
if (map.get(fieldName) != null) {
return map.get(fieldName);
}
return super.processTypeConvert(globalConfig, fieldType);
}
}
);
}
/**
* 功能描述:
* 〈 全局配置 〉
*
* @param projectPath 项目路径
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:56
*/
private static Consumer<GlobalConfig.Builder> getGlobalConfigBuilder(String projectPath) {
return builder -> {
//作者
builder.fileOverride().author("作者名称")
//输出路径(写到java目录)
.outputDir(projectPath + "\\mybatis-plus-generator\\src\\main\\java")
//开启swagger
.enableSwagger()
.commentDate("yyyy-MM-dd");
};
}
这个主要是设置你的controller、service、mapper、model的类存放父级包名
/**
* 功能描述:
* 〈 分包配置 〉
*
* @param
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:56
*/
private static Consumer<PackageConfig.Builder> getPackageConfigBuilder(String model) {
return builder -> builder.parent("com.xxx.xxx")
.moduleName(model)
.entity("model")
.other("model.data")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.mapper("mapper")
.xml("mappers")
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,
System.getProperty("user.dir") + "\\mybatis-plus-generator\\src\\main\\resources\\mappers\\web"));
}
由于每个人的风格不一样,可以根据自己的风格来配置模块
/**
* 功能描述:
* 〈 自定义模板配置 〉
*
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:57
*/
private static Consumer<InjectionConfig.Builder> getInjectionConfigBuilder() {
// 模块名称
return consumer -> {
Map<String, String> customFile = new HashMap<>(3);
customFile.put("dto"+ File.separator+"%sDTO.java", "templates/other/dto.java.ftl");
customFile.put("vo"+ File.separator+"%sVO.java", "templates/other/vo.java.ftl");
customFile.put("query"+ File.separator+"%sQuery.java", "templates/other/Query.java.ftl");
customFile.put("converter"+ File.separator+"%sConverter.java", "templates/other/converter.java.ftl");
// ui页面
customFile.put("/src/api/%s/index.ts", "templates/ui/api.java.ftl");
customFile.put("/src/commonSearchFun/%s/index.ts", "templates/ui/search.java.ftl");
customFile.put("/src/views/%s/constant/index.ts", "templates/ui/constant.java.ftl");
customFile.put("/src/views/%s/component/add-edit-dialog.vue", "templates/ui/dialog.java.ftl");
customFile.put("/src/views/%s/handler/add-edit-handler.ts", "templates/ui/addEditHandlerIndex.java.ftl");
customFile.put("/src/views/%s/handler/index.ts", "templates/ui/handlerIndex.java.ftl");
customFile.put("/src/views/%s/index.vue", "templates/ui/index.java.ftl");
consumer.customFile(customFile);
};
}
主要是配置写成的策略
/**
* 功能描述:
* 〈 策略配置 〉
*
* @param tables 表集合
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:57
*/
private static Consumer<StrategyConfig.Builder> getStrategyConfigBuilder(List<String> tables) {
return builder -> builder.addInclude(tables)
.addTablePrefix("t_", "t_web")
// 【service】配置
.serviceBuilder()
.formatServiceFileName("I%sService")
.formatServiceImplFileName("%sServiceImpl")
// 【实体类】配置
.entityBuilder()
.disableSerialVersionUID()
.superClass("基础的父类")
.enableLombok()
//删除的字段配置
.logicDeleteColumnName("is_deleted")
.logicDeletePropertyName("deleted")
.enableTableFieldAnnotation()
// 【控制器】配置
.controllerBuilder()
// 映射路径使用连字符格式,而不是驼峰
.enableHyphenStyle()
.formatFileName("%sController")
.enableRestStyle()
// 【mapper】的配置
.mapperBuilder()
.enableMapperAnnotation()
//生成通用的resultMap
.enableBaseResultMap()
.superClass("你的父类mapper")
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper");
}
public static void main(String[] args) {
// 模块
String model = "outbound";
// 表名称
List<String> tables = new ArrayList<>();
tables.add("t_web_outbound_details");
tables.add("t_web_outbound_inventory");
//枚举字段
Map<String, IColumnType> map = new HashMap<>(16);
map.put("role_type", RoleTypeEnum.USER);
// 生成代码主程序
autoGeneratorMain(tables, model, map);
}
/**
1. 功能描述:
2. 〈 生成器核心配置 〉
3. 4. @param tables 表集合
5. @param model 模块名称
6. @param map 图
7. @return : void
8. @author : yls
9. @date : 2023/4/3 14:44
10. @throws
*/
private static void autoGeneratorMain(List<String> tables,
String model, Map<String, IColumnType> map) {
// 工程目录路径
String projectPath = System.getProperty("user.dir");
// 数据库配置
DataSourceConfig.Builder dataSourceConfig = getDataSourceBuilder(map);
// 生成项目代码
FastAutoGenerator.create(dataSourceConfig)
// 全局配置
.globalConfig(getGlobalConfigBuilder(projectPath))
// 包配置
.packageConfig(getPackageConfigBuilder(model))
// 策略配置
.strategyConfig(getStrategyConfigBuilder(tables))
// 自定义DTO\VO\Query
.injectionConfig(getInjectionConfigBuilder())
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new CustomFtlTemplateEngine())
.execute();
}
@Getter
public enum RoleTypeEnum implements IColumnType {
/**
* 用户角色常量
*/
WAREHOUSE_MANAGEMENT(0, "测试001"),
AUDITOR(1, "测试002"),
USER(2, "普通用户");
@EnumValue
@JsonValue
private int code;
private String value;
RoleTypeEnum(int code, String value){
this.code = code;
this.value = value;
}
/**
* 返回枚举的名称
*/
@Override
public String getType() {
return "RoleTypeEnum";
}
/**
* 返回包路径包含名称
* @return
*/
@Override
public String getPkg() {
return "com.ushine.generator.constant.RoleTypeEnum";
}
}
/**
* @Author: yls
* @Date 2023/4/3 14:50
* @Description: 模板解析器
* @Version 1.0
**/
public class CustomFtlTemplateEngine extends FreemarkerTemplateEngine {
@Override
protected void outputCustomFile(Map<String, String> customFile, TableInfo tableInfo, Map<String, Object> objectMap) {
//数据库表映射实体名称
String entityName = tableInfo.getEntityName();
String uiSource = NameUtils.toLowerCaseFirstOne(entityName);
String uiName = NameUtils.camelToUnder(uiSource, "-");
String otherPath = this.getPathInfo(OutputFile.other);
customFile.forEach((key, value) -> {
String fileName;
String dto = "DTO.java";
String vo = "VO.java";
String query = "Query.java";
String converter = "Converter.java";
// 前端页面的生成路径
// String viewPath = System.getProperty("user.dir") + "\\mybatis-plus-generator\\src\\main\\resources\\";
String viewPath = "D:\\workspace\\xxx";
if (key.endsWith(dto) ||
key.endsWith(vo) ||
key.endsWith(query) ||
key.endsWith(converter)) {
fileName = String.format(otherPath + File.separator + key, entityName);
} else {
// 前端页面
fileName = String.format(viewPath + File.separator + key, uiName);
}
this.outputFile(new File(fileName), objectMap, value);
});
}
}
public class GeneratorMain {
/**
* 数据库地址
*/
private static final String DATA_SOURCE = "jdbc:mysql://ip:3306/数据库?userUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8";
/**
* 用户名
*/
private static final String USERNAME = "用户名";
/**
* 密码
*/
private static final String PASSWORD = "密码";
/**
* 字段名称
*/
private static String fieldName = "";
public static void main(String[] args) {
// 模块
String model = "outbound";
// 表名称
List<String> tables = new ArrayList<>();
tables.add("t_web_test");
//枚举字段: 自动生成实体类的枚举字段,可以添加多个
Map<String, IColumnType> map = new HashMap<>(16);
map.put("role_type", RoleTypeEnum.USER);
// 生成代码主程序
autoGeneratorMain(tables, model, map);
}
/**
* 功能描述:
* 〈 生成器核心配置 〉
*
* @param tables 表集合
* @param model 模块名称
* @param map 图
* @return : void
* @author : yls
* @date : 2023/4/3 14:44
* @throws
*/
private static void autoGeneratorMain(List<String> tables,
String model, Map<String, IColumnType> map) {
// 工程目录路径
String projectPath = System.getProperty("user.dir");
// 数据库配置
DataSourceConfig.Builder dataSourceConfig = getDataSourceBuilder(map);
// 生成项目代码
FastAutoGenerator.create(dataSourceConfig)
// 全局配置
.globalConfig(getGlobalConfigBuilder(projectPath))
// 包配置
.packageConfig(getPackageConfigBuilder(model))
// 策略配置
.strategyConfig(getStrategyConfigBuilder(tables))
// 自定义DTO\VO\Query
.injectionConfig(getInjectionConfigBuilder())
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new CustomFtlTemplateEngine())
.execute();
}
/**
* 功能描述:
* 〈 数据库配置 〉
*
* @param map 枚举值
* @return : com.baomidou.mybatisplus.generator.config.DataSourceConfig.Builder
* @throws
* @author : yls
* @date : 2023/4/3 9:58
*/
private static DataSourceConfig.Builder getDataSourceBuilder(Map<String, IColumnType> map) {
// 数据源配置
return new DataSourceConfig
.Builder(
DATA_SOURCE,
USERNAME,
PASSWORD)
.dbQuery(new MySqlQuery())
.keyWordsHandler(new MySqlKeyWordsHandler() {
@Override
public boolean isKeyWords(String columnName) {
//获取当前正在生成的字段名称
fieldName = columnName;
return this.getKeyWords().contains(columnName.toUpperCase(Locale.ENGLISH));
}
})
.typeConvert(new MySqlTypeConvert() {
@Override
public IColumnType processTypeConvert(GlobalConfig globalConfig,
String fieldType) {
//根据字段名称判断是否需要转换为枚举
if (map.get(fieldName) != null) {
return map.get(fieldName);
}
return super.processTypeConvert(globalConfig, fieldType);
}
}
);
}
/**
* 功能描述:
* 〈 全局配置 〉
*
* @param projectPath 项目路径
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:56
*/
private static Consumer<GlobalConfig.Builder> getGlobalConfigBuilder(String projectPath) {
return builder -> {
//作者
builder.fileOverride().author("yls")
//输出路径(写到java目录)
.outputDir(projectPath + "\\mybatis-plus-generator\\src\\main\\java")
//开启swagger
.enableSwagger()
.commentDate("yyyy-MM-dd");
};
}
/**
* 功能描述:
* 〈 分包配置 〉
*
* @param
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:56
*/
private static Consumer<PackageConfig.Builder> getPackageConfigBuilder(String model) {
return builder -> builder.parent("com.ushine.generator.web")
.moduleName(model)
.entity("model")
.other("model.data")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.mapper("mapper")
.xml("mappers")
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,
System.getProperty("user.dir") + "\\mybatis-plus-generator\\src\\main\\resources\\mappers\\web"));
}
/**
* 功能描述:
* 〈 自定义模板配置 〉
*
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:57
*/
private static Consumer<InjectionConfig.Builder> getInjectionConfigBuilder() {
// 模块名称
return consumer -> {
Map<String, String> customFile = new HashMap<>(3);
customFile.put("dto"+ File.separator+"%sDTO.java", "templates/other/dto.java.ftl");
customFile.put("vo"+ File.separator+"%sVO.java", "templates/other/vo.java.ftl");
customFile.put("query"+ File.separator+"%sQuery.java", "templates/other/Query.java.ftl");
customFile.put("converter"+ File.separator+"%sConverter.java", "templates/other/converter.java.ftl");
// ui页面
customFile.put("/src/api/%s/index.ts", "templates/ui/api.java.ftl");
customFile.put("/src/commonSearchFun/%s/index.ts", "templates/ui/search.java.ftl");
customFile.put("/src/views/%s/constant/index.ts", "templates/ui/constant.java.ftl");
customFile.put("/src/views/%s/component/add-edit-dialog.vue", "templates/ui/dialog.java.ftl");
customFile.put("/src/views/%s/handler/add-edit-handler.ts", "templates/ui/addEditHandlerIndex.java.ftl");
customFile.put("/src/views/%s/handler/index.ts", "templates/ui/handlerIndex.java.ftl");
customFile.put("/src/views/%s/index.vue", "templates/ui/index.java.ftl");
consumer.customFile(customFile);
};
}
/**
* 功能描述:
* 〈 策略配置 〉
*
* @param tables 表集合
* @return : java.util.function.Consumer
* @throws
* @author : yls
* @date : 2023/4/3 9:57
*/
private static Consumer<StrategyConfig.Builder> getStrategyConfigBuilder(List<String> tables) {
return builder -> builder.addInclude(tables)
.addTablePrefix("t_", "t_web")
// 【service】配置
.serviceBuilder()
.formatServiceFileName("I%sService")
.formatServiceImplFileName("%sServiceImpl")
// 【实体类】配置
.entityBuilder()
.disableSerialVersionUID()
.superClass("自己封装的实体父类")
.enableLombok()
.logicDeleteColumnName("is_deleted")
.logicDeletePropertyName("deleted")
.enableTableFieldAnnotation()
// 【控制器】配置
.controllerBuilder()
// 映射路径使用连字符格式,而不是驼峰
.enableHyphenStyle()
.formatFileName("%sController")
.enableRestStyle()
// 【mapper】的配置
.mapperBuilder()
.enableMapperAnnotation()
//生成通用的resultMap
.enableBaseResultMap()
.superClass("自己封装的mapper")
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper");
}
}
点击运行GeneratorMain的main方法,就会在你配置的路径下,生成如下的代码