AutoGenerator.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /**
  2. * Copyright (c) 2011-2020, hubin (jobob@qq.com).
  3. * <p>
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. * <p>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.baomidou.mybatisplus.generator;
  17. import java.io.BufferedWriter;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.OutputStreamWriter;
  22. import java.text.SimpleDateFormat;
  23. import java.util.Date;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Properties;
  28. import org.apache.ibatis.logging.Log;
  29. import org.apache.ibatis.logging.LogFactory;
  30. import org.apache.velocity.Template;
  31. import org.apache.velocity.VelocityContext;
  32. import org.apache.velocity.app.Velocity;
  33. import org.apache.velocity.app.VelocityEngine;
  34. import com.baomidou.mybatisplus.generator.config.ConstVal;
  35. import com.baomidou.mybatisplus.generator.config.FileOutConfig;
  36. import com.baomidou.mybatisplus.generator.config.TemplateConfig;
  37. import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
  38. import com.baomidou.mybatisplus.generator.config.po.TableField;
  39. import com.baomidou.mybatisplus.generator.config.po.TableInfo;
  40. import com.baomidou.mybatisplus.toolkit.CollectionUtils;
  41. import com.baomidou.mybatisplus.toolkit.StringUtils;
  42. /**
  43. * 生成文件
  44. *
  45. * @author YangHu, tangguo
  46. * @since 2016-08-30
  47. */
  48. public class AutoGenerator extends AbstractGenerator {
  49. private static final Log logger = LogFactory.getLog(AutoGenerator.class);
  50. /**
  51. * velocity引擎
  52. */
  53. private VelocityEngine engine;
  54. /**
  55. * 生成代码
  56. */
  57. public void execute() {
  58. logger.debug("==========================准备生成文件...==========================");
  59. // 初始化配置
  60. initConfig();
  61. // 创建输出文件路径
  62. mkdirs(config.getPathInfo());
  63. // 获取上下文
  64. Map<String, VelocityContext> ctxData = analyzeData(config);
  65. // 循环生成文件
  66. for (Map.Entry<String, VelocityContext> ctx : ctxData.entrySet()) {
  67. batchOutput(ctx.getKey(), ctx.getValue());
  68. }
  69. // 打开输出目录
  70. if (config.getGlobalConfig().isOpen()) {
  71. try {
  72. String osName = System.getProperty("os.name");
  73. if (osName != null) {
  74. if (osName.contains("Mac")) {
  75. Runtime.getRuntime().exec("open " + config.getGlobalConfig().getOutputDir());
  76. } else if (osName.contains("Windows")) {
  77. Runtime.getRuntime().exec("cmd /c start " + config.getGlobalConfig().getOutputDir());
  78. } else {
  79. logger.debug("文件输出目录:" + config.getGlobalConfig().getOutputDir());
  80. }
  81. }
  82. } catch (IOException e) {
  83. e.printStackTrace();
  84. }
  85. }
  86. logger.debug("==========================文件生成完成!!!==========================");
  87. }
  88. /**
  89. * <p>
  90. * 开放表信息、预留子类重写
  91. * </p>
  92. *
  93. * @param config 配置信息
  94. * @return
  95. */
  96. protected List<TableInfo> getAllTableInfoList(ConfigBuilder config) {
  97. return config.getTableInfoList();
  98. }
  99. /**
  100. * 分析数据
  101. *
  102. * @param config 总配置信息
  103. * @return 解析数据结果集
  104. */
  105. private Map<String, VelocityContext> analyzeData(ConfigBuilder config) {
  106. List<TableInfo> tableList = this.getAllTableInfoList(config);
  107. Map<String, String> packageInfo = config.getPackageInfo();
  108. Map<String, VelocityContext> ctxData = new HashMap<>();
  109. String superEntityClass = getSuperClassName(config.getSuperEntityClass());
  110. String superMapperClass = getSuperClassName(config.getSuperMapperClass());
  111. String superServiceClass = getSuperClassName(config.getSuperServiceClass());
  112. String superServiceImplClass = getSuperClassName(config.getSuperServiceImplClass());
  113. String superControllerClass = getSuperClassName(config.getSuperControllerClass());
  114. String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
  115. VelocityContext ctx;
  116. for (TableInfo tableInfo : tableList) {
  117. ctx = new VelocityContext();
  118. if (null != injectionConfig) {
  119. /**
  120. * 注入自定义配置
  121. */
  122. injectionConfig.initMap();
  123. ctx.put("cfg", injectionConfig.getMap());
  124. }
  125. /* ---------- 添加导入包 ---------- */
  126. if (config.getGlobalConfig().isActiveRecord()) {
  127. // 开启 ActiveRecord 模式
  128. tableInfo.setImportPackages("com.baomidou.mybatisplus.activerecord.Model");
  129. }
  130. if (tableInfo.isConvert()) {
  131. // 表注解
  132. tableInfo.setImportPackages("com.baomidou.mybatisplus.annotations.TableName");
  133. }
  134. if (tableInfo.isLogicDelete(config.getGlobalConfig().getLogicDeletePropertyName())) {
  135. // 逻辑删除注解
  136. tableInfo.setImportPackages("com.baomidou.mybatisplus.annotations.TableLogic");
  137. }
  138. if (StringUtils.isNotEmpty(config.getSuperEntityClass())) {
  139. // 父实体
  140. tableInfo.setImportPackages(config.getSuperEntityClass());
  141. } else {
  142. tableInfo.setImportPackages("java.io.Serializable");
  143. }
  144. // Boolean类型is前缀处理
  145. if (config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix()) {
  146. for (TableField field : tableInfo.getFields()) {
  147. if (field.getPropertyType().equalsIgnoreCase("boolean")) {
  148. if (field.getPropertyName().indexOf("is") != -1) {
  149. String noIsPropertyName = field.getPropertyName().substring(2, field.getPropertyName().length());
  150. String firstChar = noIsPropertyName.substring(0, 1).toLowerCase();
  151. String afterChar = noIsPropertyName.substring(1, noIsPropertyName.length());
  152. field.setPropertyName(config.getStrategyConfig(), firstChar + afterChar);
  153. }
  154. }
  155. }
  156. }
  157. // RequestMapping 连字符风格 user-info
  158. if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
  159. ctx.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
  160. ctx.put("controllerMappingHyphen", StringUtils.camelToHyphen(tableInfo.getEntityPath()));
  161. }
  162. ctx.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
  163. ctx.put("package", packageInfo);
  164. ctx.put("author", config.getGlobalConfig().getAuthor());
  165. ctx.put("logicDeletePropertyName", config.getGlobalConfig().getLogicDeletePropertyName());
  166. ctx.put("activeRecord", config.getGlobalConfig().isActiveRecord());
  167. ctx.put("date", date);
  168. ctx.put("table", tableInfo);
  169. ctx.put("enableCache", config.getGlobalConfig().isEnableCache());
  170. ctx.put("baseResultMap", config.getGlobalConfig().isBaseResultMap());
  171. ctx.put("baseColumnList", config.getGlobalConfig().isBaseColumnList());
  172. ctx.put("entity", tableInfo.getEntityName());
  173. ctx.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
  174. ctx.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
  175. ctx.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
  176. ctx.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
  177. ctx.put("superEntityClass", superEntityClass);
  178. ctx.put("superMapperClassPackage", config.getSuperMapperClass());
  179. ctx.put("superMapperClass", superMapperClass);
  180. ctx.put("superServiceClassPackage", config.getSuperServiceClass());
  181. ctx.put("superServiceClass", superServiceClass);
  182. ctx.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
  183. ctx.put("superServiceImplClass", superServiceImplClass);
  184. ctx.put("superControllerClassPackage", config.getSuperControllerClass());
  185. ctx.put("superControllerClass", superControllerClass);
  186. ctxData.put(tableInfo.getEntityName(), ctx);
  187. }
  188. return ctxData;
  189. }
  190. /**
  191. * 获取类名
  192. *
  193. * @param classPath
  194. * @return
  195. */
  196. private String getSuperClassName(String classPath) {
  197. if (StringUtils.isEmpty(classPath))
  198. return null;
  199. return classPath.substring(classPath.lastIndexOf(".") + 1);
  200. }
  201. /**
  202. * 处理输出目录
  203. *
  204. * @param pathInfo 路径信息
  205. */
  206. private void mkdirs(Map<String, String> pathInfo) {
  207. for (Map.Entry<String, String> entry : pathInfo.entrySet()) {
  208. File dir = new File(entry.getValue());
  209. if (!dir.exists()) {
  210. boolean result = dir.mkdirs();
  211. if (result) {
  212. logger.debug("创建目录: [" + entry.getValue() + "]");
  213. }
  214. }
  215. }
  216. }
  217. /**
  218. * 合成上下文与模板
  219. *
  220. * @param context vm上下文
  221. */
  222. private void batchOutput(String entityName, VelocityContext context) {
  223. try {
  224. TableInfo tableInfo = (TableInfo) context.get("table");
  225. Map<String, String> pathInfo = config.getPathInfo();
  226. String entityFile = String.format((pathInfo.get(ConstVal.ENTITY_PATH) + ConstVal.ENTITY_NAME), entityName);
  227. String mapperFile = String.format((pathInfo.get(ConstVal.MAPPER_PATH) + File.separator + tableInfo.getMapperName() + ConstVal.JAVA_SUFFIX), entityName);
  228. String xmlFile = String.format((pathInfo.get(ConstVal.XML_PATH) + File.separator + tableInfo.getXmlName() + ConstVal.XML_SUFFIX), entityName);
  229. String serviceFile = String.format((pathInfo.get(ConstVal.SERIVCE_PATH) + File.separator + tableInfo.getServiceName() + ConstVal.JAVA_SUFFIX), entityName);
  230. String implFile = String.format((pathInfo.get(ConstVal.SERVICEIMPL_PATH) + File.separator + tableInfo.getServiceImplName() + ConstVal.JAVA_SUFFIX), entityName);
  231. String controllerFile = String.format((pathInfo.get(ConstVal.CONTROLLER_PATH) + File.separator + tableInfo.getControllerName() + ConstVal.JAVA_SUFFIX), entityName);
  232. TemplateConfig template = config.getTemplate();
  233. // 根据override标识来判断是否需要创建文件
  234. if (isCreate(entityFile)) {
  235. vmToFile(context, template.getEntity(), entityFile);
  236. }
  237. if (isCreate(mapperFile)) {
  238. vmToFile(context, template.getMapper(), mapperFile);
  239. }
  240. if (isCreate(xmlFile)) {
  241. vmToFile(context, template.getXml(), xmlFile);
  242. }
  243. if (isCreate(serviceFile)) {
  244. vmToFile(context, template.getService(), serviceFile);
  245. }
  246. if (isCreate(implFile)) {
  247. vmToFile(context, template.getServiceImpl(), implFile);
  248. }
  249. if (isCreate(controllerFile)) {
  250. vmToFile(context, template.getController(), controllerFile);
  251. }
  252. if (injectionConfig != null) {
  253. /**
  254. * 输出自定义文件内容
  255. */
  256. List<FileOutConfig> focList = injectionConfig.getFileOutConfigList();
  257. if (CollectionUtils.isNotEmpty(focList)) {
  258. for (FileOutConfig foc : focList) {
  259. vmToFile(context, foc.getTemplatePath(), foc.outputFile(tableInfo));
  260. }
  261. }
  262. }
  263. } catch (IOException e) {
  264. logger.error("无法创建文件,请检查配置信息!", e);
  265. }
  266. }
  267. /**
  268. * 将模板转化成为文件
  269. *
  270. * @param context 内容对象
  271. * @param templatePath 模板文件
  272. * @param outputFile 文件生成的目录
  273. */
  274. private void vmToFile(VelocityContext context, String templatePath, String outputFile) throws IOException {
  275. if (StringUtils.isEmpty(templatePath)) {
  276. return;
  277. }
  278. VelocityEngine velocity = getVelocityEngine();
  279. Template template = velocity.getTemplate(templatePath, ConstVal.UTF8);
  280. File file = new File(outputFile);
  281. if (!file.getParentFile().exists()) {
  282. // 如果文件所在的目录不存在,则创建目录
  283. if (!file.getParentFile().mkdirs()) {
  284. logger.debug("创建文件所在的目录失败!");
  285. return;
  286. }
  287. }
  288. FileOutputStream fos = new FileOutputStream(outputFile);
  289. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, ConstVal.UTF8));
  290. template.merge(context, writer);
  291. writer.close();
  292. logger.debug("模板:" + templatePath + "; 文件:" + outputFile);
  293. }
  294. /**
  295. * 设置模版引擎,主要指向获取模版路径
  296. */
  297. private VelocityEngine getVelocityEngine() {
  298. if (engine == null) {
  299. Properties p = new Properties();
  300. p.setProperty(ConstVal.VM_LOADPATH_KEY, ConstVal.VM_LOADPATH_VALUE);
  301. p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
  302. p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
  303. p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
  304. p.setProperty(Velocity.OUTPUT_ENCODING, ConstVal.UTF8);
  305. p.setProperty("file.resource.loader.unicode", "true");
  306. engine = new VelocityEngine(p);
  307. }
  308. return engine;
  309. }
  310. /**
  311. * 检测文件是否存在
  312. *
  313. * @return 是否
  314. */
  315. private boolean isCreate(String filePath) {
  316. File file = new File(filePath);
  317. return !file.exists() || config.getGlobalConfig().isFileOverride();
  318. }
  319. }