提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
Mybatis-plus generator代码生成器的使用
Mybatis-plus generator是Mybatis-plus的核心功能之一,CRUD接口同样用的很多先放在后面再进行整理,官网地址在这Mybatis-plus generator。
看了下示例代码,各部分的点比较零散,准备重构一下方便后续使用。
一、Mybatis-plus generator代码生成器是什么?
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。其中主要使用到的就是这个AutoGenerator类,源码如下:
public class AutoGenerator {
/**
* 配置信息
*/
protected ConfigBuilder config;
/**
* 注入配置
*/
protected InjectionConfig injectionConfig;
/**
* 数据源配置
*/
private DataSourceConfig dataSource;
/**
* 数据库表配置
*/
private StrategyConfig strategy;
/**
* 包 相关配置
*/
private PackageConfig packageInfo;
/**
* 模板 相关配置
*/
private TemplateConfig template;
//全局 相关配置
private GlobalConfig globalConfig;
/**
* 模板引擎
*/
private AbstractTemplateEngine templateEngine;
以上属性就是需要自行配置的,简单描述一下功能,后续会详细整理。
- GlobalConfig :配置需要生成的文件地址和文件类型,包括Entity、Controller、Service、ServiceImpl、Mapper
- PackageConfig :配置包信息,包括生成文件的父目录,模块名,实体类的包名
- DataSourceConfig :配置数据源信息,包括驱动类、url、用户名和密码
- TemplateConfig :配置模板信息,这里可以进行模板的加载,需要写好模板类,支持Velocity和Freemarker
- AbstractTemplateEngine :模板引擎,这里可以指定模板引擎,同样支持Velocity和Freemarker
- StrategyConfig :配置数据库表的读取策略,包括设置转换成以驼峰形式命名,设置Lombok、rest风格Controller等
- InjectionConfig :配置自定义信息,包括自定义配置Map对象,自定义输出文件等
二、使用步骤
1.引入库
这里先引入依赖,根据官方文档,Mybatis-plus从 3.0.3 版本之后已经移除了代码生成器和模板引擎的默认依赖,需要进行手动添加,这里模板选用的是velocity(使用指南),也可以选用Freemarker(使用指南)
<!--MyBatis Plus 依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--MyBatis Plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<!--Velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
2.详细可用代码
public class MyBatisPlusGenerator {
public static void main(String[] args) {
//返回项目的真实路径地址
String projectPath = System.getProperty("user.dir");
String moduleName = scanner("模块名");
String[] tableNames = scanner("请输入表名,多个以,分隔").split(",");
//代码生成器
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(initGlobalConfig(projectPath));
autoGenerator.setDataSource(initDataSourceConfig());
autoGenerator.setPackageInfo(initPackageConfig(moduleName));
autoGenerator.setCfg(initInjectionConfig(projectPath,moduleName));
autoGenerator.setTemplate(initTemplateConfig());
autoGenerator.setStrategy(initStrategyConfig(tableNames));
autoGenerator.setTemplateEngine(new VelocityTemplateEngine());
autoGenerator.execute();
}
/**
* 读取控制台信息
*/
private static String scanner(String tip) {
Scanner sc = new Scanner(System.in);
System.out.println(("请输入" + tip + ":"));
if (sc.hasNext()) {
String next = sc.next();
if (!StringUtils.isEmpty(next)) {
return next;
}
}
throw new MybatisPlusException("请输入正确的"+tip+"!");
}
/**
* 初始化全局配置
*/
private static GlobalConfig initGlobalConfig(String projectPath) {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(projectPath+"/src/main/java");
globalConfig.setAuthor("libowen");
globalConfig.setOpen(true);
//给实体类添加Swagger2注解
globalConfig.setSwagger2(true);
//在mapper中添加Map映射
globalConfig.setBaseResultMap(true);
//覆盖原文件
globalConfig.setFileOverride(true);
//%s表示占位符,用来读取表名
globalConfig.setEntityName("%s");
globalConfig.setMapperName("%sMapper");
globalConfig.setXmlName("%sMapper");
globalConfig.setServiceName("%sService");
globalConfig.setServiceImplName("%sServiceImpl");
globalConfig.setControllerName("%sController");
globalConfig.setDateType(DateType.ONLY_DATE);
return globalConfig;
}
/**
* 初始化数据源配置
*/
private static DataSourceConfig initDataSourceConfig() {
Props props = new Props("generator.properties");
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl(props.getStr("dataSource.url"));
dataSourceConfig.setUsername(props.getStr("dataSource.username"));
dataSourceConfig.setPassword(props.getStr("dataSource.password"));
dataSourceConfig.setDriverName(props.getStr("dataSource.driverName"));
return dataSourceConfig;
}
/**
* 初始化包配置
*/
private static PackageConfig initPackageConfig(String modelName) {
Props props = new Props("generator.properties");
PackageConfig config = new PackageConfig();
config.setModuleName(modelName);
config.setParent(props.getStr("package.base"));
config.setEntity("entity");
return config;
}
/**
* 初始化模板设置
*/
private static TemplateConfig initTemplateConfig() {
TemplateConfig templateConfig = new TemplateConfig();
//可以对Controller、Entity、Service模板进行设置
//mapper.xml需要单独设置
templateConfig.setXml(null);
// templateConfig.setEntity("templates/entity2.java");
return templateConfig;
}
/**
* 初始化策略设置
*/
private static StrategyConfig initStrategyConfig(String[] tableNames) {
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
strategyConfig.setEntityLombokModel(true);
strategyConfig.setRestControllerStyle(true);
//当表名中带*号时可以启用通配符模式
if (tableNames.length == 1 && tableNames[0].contains("*")) {
String[] likeStr = tableNames[0].split("_");
String likePrefix = likeStr[0] + "_";
strategyConfig.setLikeTable(new LikeTable(likePrefix));
} else {
strategyConfig.setInclude(tableNames);
}
return strategyConfig;
}
/**
* 初始化自定义配置
*/
private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) {
InjectionConfig injectionConfig = new InjectionConfig() {
@Override
public void initMap() {
//可以用于自定义属性
}
};
//模板引擎是velocity
String templatePath = "/templates/mapper.xml.vm";
//自定义输出配置
List<FileOutConfig> fileOutConfigs = new ArrayList<>();
fileOutConfigs.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath+"/src/main/resource/mapper"+moduleName+"/"
+tableInfo.getEntityName()+"Mapper"+ StringPool.DOT_XML;
}
});
injectionConfig.setFileOutConfigList(fileOutConfigs);
return injectionConfig;
}
}
三.各配置信息类与代码实现
1、GlobalConfig 全局配置类
public class GlobalConfig {
private String outputDir = "D://"; //配置文件输出目录
private boolean fileOverride = false; //是否允许覆盖
private boolean open = true; //是否自动打开输出目录
private boolean enableCache = false; //是否在xml中添加二级缓存配置
private String author; //添加作者
private boolean swagger2 = false; //是否支持swagger2
private boolean baseResultMap = false; //是否创建baseResultMap
private DateType dateType = DateType.TIME_PACK; //数据库类型到实体的类型对应策略
private String entityName; //%s作为占位符
private String mapperName; //%sMapper
private String xmlName; //%s.xml
private String serviceName; //%sService
private String serviceImplName; //%sServiceImpl
private String controllerName; //%sController
private IdType idType; //指定生成的主键的Id类型
}
这里需要注意的是,使用如entityName时,可以使用 ‘’%s‘’ 作为占位符,例如: %sAction 生成 UserAction,mapperName设置成%sMapper等。
2、PackageConfig 包配置类
public class PackageConfig {
private String parent = "com.baomidou"; //这里配置父包名
private String moduleName = ""; //配置模块包名
private String entity = "entity"; //配置实体包名
private String service = "service"; //配置service包名
private String serviceImpl = "service.impl"; //配置serviceImpl报名
private String mapper = "mapper"; //配置mapper包名
private String xml = "mapper.xml"; //配置mapper.xml包名
private String controller = "controller"; //配置controller包名
private Map<String, String> pathInfo; //配置路径信息
public String getParent() { //获取服包名
if (StringUtils.isNotBlank(moduleName)) {
return parent + StringPool.DOT + moduleName;
}
return parent;
}
}
包配置文件配置方法如下:
import cn.hutool.setting.dialect.Props;
private static PackageConfig initPackageConfig(String modelName) {
//...
PackageConfig config = new PackageConfig();
config.setModuleName(modelName);
config.setParent("这里填包名"); //如com.xxx.edu.model
config.setEntity("entity");
return config;
}
这里没有设置controller、service、serviceImpl、mapper等信息,使用默认名称生成对应包名即可。
Props类的使用,Hutool包中的Props对象封装了一些使用Properties的方法,并添加了PropsUtil相关工具类,用于对properties我文件进行缓存。
Props加载properties文件的流程大致如下,省略了部分代码
public class MyPropertiesLoader extends Properties {
...
public MyPropertiesLoader(String path, Charset charset) {
if (StringUtils.isBlank(path)) {
Assert.notBlank(path, "Blank properties file path !");
}
//ResourceUtil.getReader 会以相对于classpath绝对路径以文件流的形式返回URL
//根据路径创建File对象并创建FileResource流
BufferedReader reader = ResourceUtil.getReader(path,charset);
this.load(reader);
}
public void load(BufferedReader reader) {
try {
//调用Properties类的load方法,加载properties文件
super.load(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取properties文件中的key
public String getStr(String key) {
return getProperty(key);
}
}
3、DataSourceConfig :配置数据源信息,包括驱动类、url、用户名和密码
public class DataSourceConfig {
private IDbQuery dbQuery; //数据库信息查询
private DbType dbType; //数据库类型
private String schemaName; //PostgreSQL schemaName
private ITypeConvert typeConvert; //类型转换
private IKeyWordsHandler keyWordsHandler; //关键字处理器
private String url; //驱动连接的URL
private String driverName; //驱动名称
private String username; //数据库连接用户名
private String password; //数据库连接密码
...
}
在设置url和driverName时也是有坑存在,引入一篇比较好的回答,这里就不再赘述
> mysql-connector-java与mysql驱动冲突问题
4、TemplateConfig :模板信息配置类
public class TemplateConfig {
@Getter(AccessLevel.NONE)
private String entity = ConstVal.TEMPLATE_ENTITY_JAVA; //对应路径"/templates/entity.java"
private String entityKt = ConstVal.TEMPLATE_ENTITY_KT; //对应路径"/templates/entity.kt";
private String service = ConstVal.TEMPLATE_SERVICE; //对应路径"/templates/mapper.java";
private String serviceImpl = ConstVal.TEMPLATE_SERVICE_IMPL;
private String mapper = ConstVal.TEMPLATE_MAPPER;
private String xml = ConstVal.TEMPLATE_XML;
private String controller = ConstVal.TEMPLATE_CONTROLLER;
...
}
之后找到templates下的模板
5、AbstractTemplateEngine :模板引擎类
该抽象类下有三种实现方式,根据所加载的引擎类型加载对应的文件类型。
6、StrategyConfig :数据库表读取策略
由于该类下的属性太多,选择部分列出
public class StrategyConfig {
/**
* 数据库表映射到实体的命名策略
*/
private NamingStrategy naming = NamingStrategy.no_change;
/**
* 数据库表映射到实体的命名策略
*/
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;
/**
* 【实体】是否为lombok模型(默认 false)<br>
* <a href="https://projectlombok.org/">document</a>
*/
private boolean entityLombokModel = false;
/**
* @Controller->@RestControlle
*/
private boolean restControllerStyle = false;
/**
* 驼峰转连字符
* @RequestMapping("/managerUserActionHistory") ---> @RequestMapping("/manager-user-action-history")
*/
private boolean controllerMappingHyphenStyle = false;
/**
* 是否生成实体时,生成字段注解
*/
private boolean entityTableFieldAnnotationEnable = false;
/**
* 乐观锁属性名称
*/
private String versionFieldName;
...
}
经常会使用到其中驼峰转字符的方法,需要同时配置才可以实现该功能
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
7、InjectionConfig :自定义信息配置
代码如下:
public abstract class InjectionConfig {
/**
* 全局配置
*/
private ConfigBuilder config;
/**
* 自定义返回配置 Map 对象
*/
private Map<String, Object> map;
/**
* 自定义输出文件
*/
private List<FileOutConfig> fileOutConfigList;
/**
* 自定义判断是否创建文件
*/
private IFileCreate fileCreate;
/**
* 注入自定义 Map 对象,针对所有表的全局参数
*/
public abstract void initMap();
...
}
在这里配置xml使用的模板样式,以及输出xml文件的目录
四、总结
在日常开发当中,使用代码生成器可以大大降低开发工作中的机械任务,把工作重心放在业务逻辑的处理上。每一项技术的诞生总有它能解决的问题,类似人工智能所说,没有最好的算法,只有最适合解决当前问题的算法。本文只对代码生成器中的部分应用进行总结,还有更多应用场景需要之后补充。
此文主要根据Github上的mall项目进行学习,此后也将陆续学习该项目中各模板的使用流程和细节,力求从需求设计、业务逻辑、架构设计等多方面进行掌握。