教你用Java实现一个简单的代码生成器
前言
逆向工程从数据库表直接生成代码,是日常开发中常用的敏捷开发手段,常见的例如:mybatis-plus的代码生成器等
为什么要自己写代码生成器呢?MP的生成器不香吗?香!
但是自己写的工具用起来最顺手,可以随意扩展,想怎么玩就怎么玩,只要自己有想法,玩出花来都没问题,当然了,能力有限,现在还只能实现简单版本,更多骚操作自己发挥!
思路:
1、建立jdbc连接,执行查询sql,获取表结构信息。
2、在指定的路径上创建文件。
3、按照我们的布局排版要求,根据表结构信息拼接文件的内容。
4、将字符输出到文件中。
以上即可完成一个文件的自动生成
编码
通用部分
几个内部工具类
文章图片
file工具类:创建、读取文件
字符串工具类:驼峰标识、下划线互转,首字母大写,数据库字段类型转java类型等
jdbc连接:连接数据库
表注释、表结构信息实体类、执行sql获取表结构信息的方法
文章图片
表结构信息
private String columnName; //字段名private String dataType; //字段类型private String columnComment; //字段注释private String columnKey; //主键private String extra; //主键类型
mysql查询表注释、表字段信息使用的是
表字段信息
SELECTcolumn_name,data_type,column_comment,column_key,extra FROMinformation_schema.COLUMNS WHEREtable_schema = (SELECT DATABASE()) AND table_name =?
表注释
SELECTtable_comment FROMinformation_schema.TABLES WHEREtable_schema = (SELECT DATABASE()) AND table_name =?
需要支持其他数据库类型的,自己调整就好了,例如oracle获取表注释、表结构sql如下:
-- 表、表注释SELECTt.table_name,t1.comments FROMuser_tables tJOIN user_tab_comments t1 ON t.table_name = t1.table_name; -- 表字段、字段注释SELECTt.table_name,c.column_name,c.data_type,cc.comments FROMUSER_TAB_COLUMNS cJOIN user_tables t ON c.table_name = t.table_nameJOIN user_col_comments cc ON cc.table_name = t.table_name WHEREcc.column_name = c.column_name;
另外,数据连接、基础路径的配置也是一样
/** * 数据连接相关,需要手动设置 */private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8"; private static final String USERNAME = "root"; private static final String PASSWORD = "123456"; private static final String DRIVER_CLASSNAME = "com.mysql.cj.jdbc.Driver"; /** * 基础路径,需要手动设置 */private String basePackage = "cn\\huanzi\\qch\\baseadmin\\"; //根包位置private String filePackage = basePackage + "sys\\"; //文件所在包位置
2.0版本多一个模板文件路径
private String tlfPath = System.getProperty("user.dir") + "\\src\\main\\resources\\tlf\\"; //模板文件位置
main函数也一样,调用构造参数,传入表名,调用入口函数
public static void main(String[] args) {//String[] tables = {"sys_user","sys_menu","sys_authority","sys_user_menu","sys_user_authority","sys_shortcut_menu","sys_setting"}; String[] tables = {"tb_user"}; for (String table : tables) {String msg = new AutoGenerator(table).create(); System.out.println(msg); }}
V1.0版本
AutoGenerator,1.0版本采用原始的在代码拼接字符串,然后创建文件将字符串输出的方法,比较原始但个人觉得可玩性较高
几个创建方法,就拿实体类来举例
文章图片
/** * 创建pojo实体类 */private void createPojo(ListtableInfos) {//创建文件File file = FileUtil.createFile(filePath + "pojo\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ".java"); //拼接文件内容StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "pojo; \n" +"\n" +"import lombok.Data; \n" +"import javax.persistence.*; \n" +"import java.io.Serializable; \n" +"import java.util.Date; \n" +"\n" +"@Entity\n" +"@Table(name = \"" + tableName + "\")\n" +"@Data\n" +"public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {\n"); //遍历设置属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {stringBuffer.append("@Id\n"); }//自增if ("auto_increment".equals(tableInfo.getExtra())) {stringBuffer.append("@GeneratedValue(strategy= GenerationType.IDENTITY)\n"); }stringBuffer.append("private ").append(StringUtil.typeMapping(tableInfo.getDataType())).append(" ").append(StringUtil.camelCaseName(tableInfo.getColumnName())).append("; //").append(tableInfo.getColumnComment()).append("\n\n"); }stringBuffer.append("}"); //写入文件内容FileUtil.fileWriter(file, stringBuffer); }
其他的也一样,无非就:创建文件、拼接文件内容、输出文件内容
入口函数,供main函数直接调用
/** * 快速创建,供外部调用,调用之前先设置一下项目的基础路径 */private String create() {System.out.println("生成路径位置:" + filePath); //获取表信息ListtableInfo = getTableInfo(); //开始生成代码createPojo(tableInfo); createVo(tableInfo); createRepository(tableInfo); createService(tableInfo); createController(tableInfo); return tableName + " 后台代码生成完毕!"; }
V2.0版本
AutoGeneratorPlus,2.0版本升级了,设置了模板文件、文件内容的字符串从模板读取,再根据关键字替换参数,最后再输出到创建的文件中,这个版本就比较好理解,大部分的代码生成器也都这样干
需要先定义模板文件(文件名后缀无所谓,自己随便定义),拿entity来举例
文章图片
package cn.huanzi.qch.baseadmin.sys.${entityToLowerCase}.pojo; import lombok.Data; import javax.persistence.*; import java.io.Serializable; import java.util.Date; /** * ${tableComment} 实体类 * * ${author} * ${date} */@Entity@Table(name = "${tableName}")@Datapublic class ${entity} implements Serializable {#for#ifPri#ifAutoIncrementprivate ${tableInfo.dataType} ${tableInfo.columnName}; //${tableInfo.columnComment}#end}
${},用于取参数,替换成我们的值
#for、#if,循环遍历表字段以及判断是否为主键、是否主键自增
各种关键字随便定义,我们在读取模板文件处理时能对上就行
文件内容处理
文章图片
/** * 读取模板,设置内容,生成文件 * @param templatePath 模板文件路径 * @param outputFile 文件生成路径 * @param tableInfos 表字段信息 * @param customParameter 自定义参数 */private void writer(String templatePath, String outputFile,ListtableInfos,Map customParameter){//主键TableInfo prikey = new TableInfo(); //for循环标识boolean forFlag = false; StringBuilder forContent = new StringBuilder(); //驼峰标识映射后的表名String replacement = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //遍历属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {prikey = tableInfo; break; }}try(FileReader fileReader = new FileReader(templatePath); BufferedReader reader = new BufferedReader(fileReader)) {//生成文件File file = FileUtil.createFile(outputFile); StringBuffer stringBuffer = new StringBuffer(); //读取模板文件,拼接文件内容Object[] lines = reader.lines().toArray(); for (Object o : lines) {String line = String.valueOf(o); /* 设置值 *///${tableName} 表名称,例如:tb_userif(line.contains("${tableName}")){line = line.replaceAll("\\$\\{tableName}", tableName); }//${tableComment} 表注释,例如:tb_userif(line.contains("${tableComment}")){line = line.replaceAll("\\$\\{tableComment}", tableComment); }//${entity} 实体类名称,例如:TbUserif(line.contains("${entity}")){line = line.replaceAll("\\$\\{entity}", replacement); }//${entityFirstToLowerCase} 实体类名称首字母小写,例如:tbUserif(line.contains("${entityFirstToLowerCase}")){line = line.replaceAll("\\$\\{entityFirstToLowerCase}", StringUtil.camelCaseName(tableName)); }//${entityToLowerCase} 实体类名称全小写,例如:tbuserif(line.contains("${entityToLowerCase}")){line = line.replaceAll("\\$\\{entityToLowerCase}", replacement.toLowerCase()); }//${priDataType} 实体类主键类型,例如:Stringif(line.contains("${priDataType}")){line = line.replaceAll("\\$\\{priDataType}", StringUtil.typeMapping(prikey.getDataType())); }//处理自定义参数line = customParameter(line,customParameter); //先取得循环体的内容if(forFlag){forContent.append(line).append("\n"); }//是否为for循环遍历表字段if(line.contains("#for")){forFlag = true; }if(line.contains("#end")){forFlag = false; line = line.replaceAll("#end", ""); }//遍历循环体的内容,并设置值if(!forFlag && forContent.length() > 0){//遍历表字段for (TableInfo tableInfo : tableInfos) {String tableColumns = forContent.toString()//表字段信息:类型、名称、注释.replaceAll("\\$\\{tableInfo.dataType}", StringUtil.typeMapping(tableInfo.getDataType())).replaceAll("\\$\\{tableInfo.columnName}", StringUtil.camelCaseName(tableInfo.getColumnName())).replaceAll("\\$\\{tableInfo.columnComment}", tableInfo.getColumnComment()); //清除多余#end,以及换行符tableColumns = tableColumns.replaceAll("#end", "").replaceAll("\n", ""); //设置是否主键、是否自增String pri = "",autoIncrement=""; //主键if ("PRI".equals(tableInfo.getColumnKey())) {pri = " @Id\n"; //自增idif ("auto_increment".equals(tableInfo.getExtra())){autoIncrement = "@GeneratedValue(strategy= GenerationType.IDENTITY)\n"; }}tableColumns = tableColumns.replaceAll("#ifPri", pri).replaceAll("#ifAutoIncrement", autoIncrement); //处理自定义参数tableColumns = customParameter(tableColumns,customParameter); //前补tab,后补换行符stringBuffer.append("").append(tableColumns.trim()).append("\n\n"); }//置空forContent.setLength(0); }if(!forFlag){stringBuffer.append(line).append("\n"); }}//写入数据到到文件中FileUtil.fileWriter(file, stringBuffer); }catch (Exception e){e.printStackTrace(); }}
内置了几个重要参数
${tableName} 表名称,例如:tb_user${tableComment} 表注释,例如:tb_user${entity} 实体类名称,例如:TbUser${entityFirstToLowerCase} 实体类名称首字母小写,例如:tbUser${entityToLowerCase} 实体类名称全小写,例如:tbuser${priDataType} 实体类主键类型,例如:String还有三个表字段信息:类型、名称、注释${tableInfo.dataType}${tableInfo.columnName}${tableInfo.columnComment}
支持自定义参数Map customParameter,例如模板文件中的注释:
/** * ${author} * ${date} */
入口函数
/** * 快速创建,供外部调用,调用之前先设置一下项目的基础路径 */private String create() {System.out.println("生成路径位置:" + filePath); //获取表信息ListtableInfo = getTableInfo(); //驼峰标识映射后的表名String captureName = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //自定义参数HashMap customParameter = new HashMap<>(); customParameter.put("author","作者:Auto Generator By 'huanzi-qch'"); customParameter.put("date","生成日期:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); //读取模板、生成代码writer(tlfPath+"controller.tlf",filePath + "controller\\" + captureName + "Controller.java",tableInfo,customParameter); writer(tlfPath+"entity.tlf",filePath + "pojo\\" + captureName + ".java",tableInfo,customParameter); writer(tlfPath+"entityvo.tlf",filePath + "vo\\" + captureName + "Vo.java",tableInfo,customParameter); writer(tlfPath+"repository.tlf",filePath + "repository\\" + captureName + "Repository.java",tableInfo,customParameter); writer(tlfPath+"service.tlf",filePath + "service\\" + captureName + "Service.java",tableInfo,customParameter); writer(tlfPath+"serviceimpl.tlf",filePath + "service\\" + captureName + "ServiceImpl.java",tableInfo,customParameter); return tableName + " 后台代码生成完毕!"; }
比较复杂的就是#for、#if的处理,我这里只是简单实现,不过也完全够我们用了
效果 V1.0版本
文章图片
V2.0版本
文章图片
后记 大部分项目的代码都是可以复用的,特别是像我们这种封装了一套通用代码,单表直接继承实现CRUD、分页等功能,每个模块高度相似的代码,代码生成器就成了敏捷开发中重要的一步,直接根据数据库表生成我们想要的代码,省去了一步步创建文件、复制粘贴文件内容的繁琐步骤,实现快速开发!
自己写的代码生成器,扩展性更强,满足每个业务模块的代码要求不成问题
开源 在这里贴出完整代码,全都在一个类里面,并且没有其他依赖包,很纯!
v1.0AutoGenerator
文章图片
View Code
package cn.huanzi.qch.baseadmin.autogenerator; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * 代码生成工具 V1.0 */public class AutoGenerator {/*** 程序自动设置*/private String tableName; //表名private String tableComment; //表注释private String filePath; //最终文件生成位置/*** 数据连接相关,需要手动设置*/private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8"; private static final String USERNAME = "root"; private static final String PASSWORD = "123456"; private static final String DRIVER_CLASSNAME = "com.mysql.cj.jdbc.Driver"; /*** 基础路径,需要手动设置*/private String basePackage = "cn\\huanzi\\qch\\baseadmin\\"; //根包位置private String filePackage = basePackage + "sys\\"; //文件所在包位置/*** 构造参数,设置表名*/private AutoGenerator(String tableName) {//设置表名this.tableName = tableName; //文件所在包位置filePackage = filePackage + StringUtil.camelCaseName(tableName).toLowerCase() + "\\"; //拼接完整最终位置 System.getProperty("user.dir") 获取的是项目所在路径,如果我们是子项目,则需要添加一层路径filePath = System.getProperty("user.dir") + "\\src\\main\\java\\" + filePackage; }/*** 创建pojo实体类*/private void createPojo(ListtableInfos) {//创建文件File file = FileUtil.createFile(filePath + "pojo\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ".java"); //拼接文件内容StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "pojo; \n" +"\n" +"import lombok.Data; \n" +"import javax.persistence.*; \n" +"import java.io.Serializable; \n" +"import java.util.Date; \n" +"\n" +"@Entity\n" +"@Table(name = \"" + tableName + "\")\n" +"@Data\n" +"public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {\n"); //遍历设置属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {stringBuffer.append("@Id\n"); }//自增if ("auto_increment".equals(tableInfo.getExtra())) {stringBuffer.append("@GeneratedValue(strategy= GenerationType.IDENTITY)\n"); }stringBuffer.append("private ").append(StringUtil.typeMapping(tableInfo.getDataType())).append(" ").append(StringUtil.camelCaseName(tableInfo.getColumnName())).append("; //").append(tableInfo.getColumnComment()).append("\n\n"); }stringBuffer.append("}"); //写入文件内容FileUtil.fileWriter(file, stringBuffer); }/*** 创建vo类*/private void createVo(List tableInfos) {File file = FileUtil.createFile(filePath + "vo\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo.java"); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "vo; \n" +"\n" +"import "+ basePackage.replaceAll("\\\\", ".") +" common.pojo.PageCondition; "+"import lombok.Data; \n" +"import java.io.Serializable; \n" +"import java.util.Date; \n" +"\n" +"@Data\n" +"public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo extends PageCondition implements Serializable {\n"); //遍历设置属性for (TableInfo tableInfo : tableInfos) {stringBuffer.append("private ").append(StringUtil.typeMapping(tableInfo.getDataType())).append(" ").append(StringUtil.camelCaseName(tableInfo.getColumnName())).append("; //").append(tableInfo.getColumnComment()).append("\n\n"); }stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); }/*** 创建repository类*/private void createRepository(List tableInfos) {File file = FileUtil.createFile(filePath + "repository\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {t = StringUtil.typeMapping(tableInfo.getDataType()); }}stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "repository; \n" +"\n" +"import " + basePackage.replaceAll("\\\\", ".") + "common.repository.*; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "; \n" +"import org.springframework.stereotype.Repository; \n" +"\n" +"@Repository\n" +"public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"); stringBuffer.append("\n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); }/*** 创建service类*/private void createService(List tableInfos) {File file = FileUtil.createFile(filePath + "service\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {t = StringUtil.typeMapping(tableInfo.getDataType()); }}stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "service; \n" +"\n" +"import " + basePackage.replaceAll("\\\\", ".") + "common.service.*; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo; \n" +"\n" +"public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"); stringBuffer.append("\n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); //ImplFile file1 = FileUtil.createFile(filePath + "service\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl.java"); StringBuffer stringBuffer1 = new StringBuffer(); stringBuffer1.append("package " + filePackage.replaceAll("\\\\", ".") + "service; \n" +"\n" +"import " + basePackage.replaceAll("\\\\", ".") + "common.service.*; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository; \n" +"import org.springframework.beans.factory.annotation.Autowired; \n" +"import org.springframework.stereotype.Service; \n" +"import org.springframework.transaction.annotation.Transactional; \n" +"import javax.persistence.EntityManager; \n" +"import javax.persistence.PersistenceContext; \n" +"\n" +"@Service\n" +"@Transactional\n" +"public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl extends CommonServiceImpl<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> implements " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service{"); stringBuffer1.append("\n\n"); stringBuffer1.append("@PersistenceContext\n" +"private EntityManager em; \n"); stringBuffer1.append("" +"@Autowired\n" +"private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository; \n"); stringBuffer1.append("}"); FileUtil.fileWriter(file1, stringBuffer1); }/*** 创建controller类*/private void createController(List tableInfos) {File file = FileUtil.createFile(filePath + "controller\\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller.java"); StringBuffer stringBuffer = new StringBuffer(); String t = "String"; //遍历属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {t = StringUtil.typeMapping(tableInfo.getDataType()); }}stringBuffer.append("package " + filePackage.replaceAll("\\\\", ".") + "controller; \n" +"\n" +"import " + basePackage.replaceAll("\\\\", ".") + "common.controller.*; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo; \n" +"import " + filePackage.replaceAll("\\\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service; \n" +"import org.springframework.beans.factory.annotation.Autowired; \n" +"import org.springframework.web.bind.annotation.*; \n" +"\n" +"@RestController\n" +"@RequestMapping(\"/sys/" + StringUtil.camelCaseName(tableName) + "/\")\n" +"public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"); stringBuffer.append("\n"); stringBuffer.append("" +"@Autowired\n" +"private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service; \n"); stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); }/*** file工具类*/private static class FileUtil {/*** 创建文件** @param pathNameAndFileName 路径跟文件名* @return File对象*/private static File createFile(String pathNameAndFileName) {File file = new File(pathNameAndFileName); try {//获取父目录File fileParent = file.getParentFile(); if (!fileParent.exists()) {fileParent.mkdirs(); }//创建文件if (!file.exists()) {file.createNewFile(); }} catch (Exception e) {file = null; System.err.println("新建文件操作出错"); e.printStackTrace(); }return file; }/*** 字符流写入文件** @param filefile对象* @param stringBuffer 要写入的数据*/private static void fileWriter(File file, StringBuffer stringBuffer) {//字符流try {FileWriter resultFile = new FileWriter(file, false); //true,则追加写入 false,则覆盖写入PrintWriter myFile = new PrintWriter(resultFile); //写入myFile.println(stringBuffer.toString()); myFile.close(); resultFile.close(); } catch (Exception e) {System.err.println("写入操作出错"); e.printStackTrace(); }}}/*** 字符串处理工具类*/private static class StringUtil {/*** 数据库类型->JAVA类型** @param dbType 数据库类型* @return JAVA类型*/private static String typeMapping(String dbType) {String javaType; if ("int|integer".contains(dbType)) {javaType = "Integer"; } else if ("float|double|decimal|real".contains(dbType)) {javaType = "Double"; } else if ("date|time|datetime|timestamp".contains(dbType)) {javaType = "Date"; } else {javaType = "String"; }return javaType; }/*** 驼峰转换为下划线*/private static String underscoreName(String camelCaseName) {StringBuilder result = new StringBuilder(); if (camelCaseName != null && camelCaseName.length() > 0) {result.append(camelCaseName.substring(0, 1).toLowerCase()); for (int i = 1; i < camelCaseName.length(); i++) {char ch = camelCaseName.charAt(i); if (Character.isUpperCase(ch)) {result.append("_"); result.append(Character.toLowerCase(ch)); } else {result.append(ch); }}}return result.toString(); }/*** 首字母大写*/private static String captureName(String name) {char[] cs = name.toCharArray(); cs[0] -= 32; return String.valueOf(cs); }/*** 下划线转换为驼峰*/private static String camelCaseName(String underscoreName) {StringBuilder result = new StringBuilder(); if (underscoreName != null && underscoreName.length() > 0) {boolean flag = false; for (int i = 0; i < underscoreName.length(); i++) {char ch = underscoreName.charAt(i); if ("_".charAt(0) == ch) {flag = true; } else {if (flag) {result.append(Character.toUpperCase(ch)); flag = false; } else {result.append(ch); }}}}return result.toString(); }}/*** JDBC连接数据库工具类*/private static class DBConnectionUtil {static {// 1、加载驱动try {Class.forName(DRIVER_CLASSNAME); } catch (ClassNotFoundException e) {e.printStackTrace(); }}/*** 返回一个Connection连接*/static Connection getConnection() {Connection conn = null; // 2、连接数据库try {conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); } catch (SQLException e) {e.printStackTrace(); }return conn; }/*** 关闭Connection,Statement连接*/public static void close(Connection conn, Statement stmt) {try {conn.close(); stmt.close(); } catch (SQLException e) {e.printStackTrace(); }}/*** 关闭Connection,Statement,ResultSet连接*/public static void close(Connection conn, Statement stmt, ResultSet rs) {try {close(conn, stmt); rs.close(); } catch (SQLException e) {e.printStackTrace(); }}}/*** 表结构信息实体类*/private class TableInfo {private String columnName; //字段名private String dataType; //字段类型private String columnComment; //字段注释private String columnKey; //主键private String extra; //主键类型public String getColumnName() {return columnName; }public void setColumnName(String columnName) {this.columnName = columnName; }public String getDataType() {return dataType; }public void setDataType(String dataType) {this.dataType = dataType; }public String getColumnComment() {return columnComment; }public void setColumnComment(String columnComment) {this.columnComment = columnComment; }public String getColumnKey() {return columnKey; }public void setColumnKey(String columnKey) {this.columnKey = columnKey; }public String getExtra() {return extra; }public void setExtra(String extra) {this.extra = extra; }}/*** 获取表结构信息* 目前仅支持mysql*/private List getTableInfo() {Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; ArrayList list = new ArrayList<>(); try {conn = DBConnectionUtil.getConnection(); //表字段信息String sql = "select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_schema = (select database()) and table_name=?"; ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) {TableInfo tableInfo = new TableInfo(); //列名,全部转为小写tableInfo.setColumnName(rs.getString("column_name").toLowerCase()); //列类型tableInfo.setDataType(rs.getString("data_type")); //列注释tableInfo.setColumnComment(rs.getString("column_comment")); //主键tableInfo.setColumnKey(rs.getString("column_key")); //主键类型tableInfo.setExtra(rs.getString("extra")); list.add(tableInfo); }//表注释sql = "select table_comment from information_schema.tables where table_schema = (select database()) and table_name=?"; ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) {//表注释tableComment = rs.getString("table_comment"); }} catch (SQLException e) {e.printStackTrace(); } finally {if(rs != null){DBConnectionUtil.close(conn, ps, rs); }}return list; }/*** 快速创建,供外部调用,调用之前先设置一下项目的基础路径*/private String create() {System.out.println("生成路径位置:" + filePath); //获取表信息List tableInfo = getTableInfo(); //开始生成代码createPojo(tableInfo); createVo(tableInfo); createRepository(tableInfo); createService(tableInfo); createController(tableInfo); return tableName + " 后台代码生成完毕!"; }public static void main(String[] args) {//String[] tables = {"sys_user","sys_menu","sys_authority","sys_user_menu","sys_user_authority","sys_shortcut_menu","sys_setting"}; String[] tables = {"tb_user"}; for (String table : tables) {String msg = new AutoGenerator(table).create(); System.out.println(msg); }}}
v2.0AutoGeneratorPlus
文章图片
View Code
package cn.huanzi.qch.baseadmin.autogenerator; import java.io.*; import java.sql.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 代码生成工具 V2.0 */public class AutoGeneratorPlus {/*** 程序自动设置*/private String tableName; //表名private String tableComment; //表注释private String filePath; //最终文件生成位置/*** 数据连接相关,需要手动设置*/private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8"; private static final String USERNAME = "root"; private static final String PASSWORD = "123456"; private static final String DRIVER_CLASSNAME = "com.mysql.cj.jdbc.Driver"; /*** 基础路径,需要手动设置*/private String tlfPath = System.getProperty("user.dir") + "\\src\\main\\resources\\tlf\\"; //模板文件位置private String basePackage = "cn\\huanzi\\qch\\baseadmin\\"; //根包位置private String filePackage = basePackage + "sys\\"; //文件所在包位置/*** 构造参数,设置表名*/private AutoGeneratorPlus(String tableName) {//设置表名this.tableName = tableName; //文件所在包位置filePackage = filePackage + StringUtil.camelCaseName(tableName).toLowerCase() + "\\"; //拼接完整最终位置 System.getProperty("user.dir") 获取的是项目所在路径,如果我们是子项目,则需要添加一层路径filePath = System.getProperty("user.dir") + "\\src\\main\\java\\" + filePackage; }/*** 读取模板,设置内容,生成文件* @param templatePath 模板文件路径* @param outputFile 文件生成路径* @param tableInfos 表字段信息* @param customParameter 自定义参数*/private void writer(String templatePath, String outputFile,ListtableInfos,Map customParameter){//主键TableInfo prikey = new TableInfo(); //for循环标识boolean forFlag = false; StringBuilder forContent = new StringBuilder(); //驼峰标识映射后的表名String replacement = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //遍历属性for (TableInfo tableInfo : tableInfos) {//主键if ("PRI".equals(tableInfo.getColumnKey())) {prikey = tableInfo; break; }}try(FileReader fileReader = new FileReader(templatePath); BufferedReader reader = new BufferedReader(fileReader)) {//生成文件File file = FileUtil.createFile(outputFile); StringBuffer stringBuffer = new StringBuffer(); //读取模板文件,拼接文件内容Object[] lines = reader.lines().toArray(); for (Object o : lines) {String line = String.valueOf(o); /* 设置值 *///${tableName} 表名称,例如:tb_userif(line.contains("${tableName}")){line = line.replaceAll("\\$\\{tableName}", tableName); }//${tableComment} 表注释,例如:tb_userif(line.contains("${tableComment}")){line = line.replaceAll("\\$\\{tableComment}", tableComment); }//${entity} 实体类名称,例如:TbUserif(line.contains("${entity}")){line = line.replaceAll("\\$\\{entity}", replacement); }//${entityFirstToLowerCase} 实体类名称首字母小写,例如:tbUserif(line.contains("${entityFirstToLowerCase}")){line = line.replaceAll("\\$\\{entityFirstToLowerCase}", StringUtil.camelCaseName(tableName)); }//${entityToLowerCase} 实体类名称全小写,例如:tbuserif(line.contains("${entityToLowerCase}")){line = line.replaceAll("\\$\\{entityToLowerCase}", replacement.toLowerCase()); }//${priDataType} 实体类主键类型,例如:Stringif(line.contains("${priDataType}")){line = line.replaceAll("\\$\\{priDataType}", StringUtil.typeMapping(prikey.getDataType())); }//处理自定义参数line = customParameter(line,customParameter); //先取得循环体的内容if(forFlag){forContent.append(line).append("\n"); }//是否为for循环遍历表字段if(line.contains("#for")){forFlag = true; }if(line.contains("#end")){forFlag = false; line = line.replaceAll("#end", ""); }//遍历循环体的内容,并设置值if(!forFlag && forContent.length() > 0){//遍历表字段for (TableInfo tableInfo : tableInfos) {String tableColumns = forContent.toString()//表字段信息:类型、名称、注释.replaceAll("\\$\\{tableInfo.dataType}", StringUtil.typeMapping(tableInfo.getDataType())).replaceAll("\\$\\{tableInfo.columnName}", StringUtil.camelCaseName(tableInfo.getColumnName())).replaceAll("\\$\\{tableInfo.columnComment}", tableInfo.getColumnComment()); //清除多余#end,以及换行符tableColumns = tableColumns.replaceAll("#end", "").replaceAll("\n", ""); //设置是否主键、是否自增String pri = "",autoIncrement=""; //主键if ("PRI".equals(tableInfo.getColumnKey())) {pri = " @Id\n"; //自增idif ("auto_increment".equals(tableInfo.getExtra())){autoIncrement = "@GeneratedValue(strategy= GenerationType.IDENTITY)\n"; }}tableColumns = tableColumns.replaceAll("#ifPri", pri).replaceAll("#ifAutoIncrement", autoIncrement); //处理自定义参数tableColumns = customParameter(tableColumns,customParameter); //前补tab,后补换行符stringBuffer.append("").append(tableColumns.trim()).append("\n\n"); }//置空forContent.setLength(0); }if(!forFlag){stringBuffer.append(line).append("\n"); }}//写入数据到到文件中FileUtil.fileWriter(file, stringBuffer); }catch (Exception e){e.printStackTrace(); }}private void writer(String templatePath, String outputFile,List tableInfos){writer(templatePath,outputFile,tableInfos,new HashMap<>()); }/*** 处理自定义参数*/private String customParameter(String str,Map customParameter){for (String key : customParameter.keySet()) {str = str.replaceAll("\\$\\{"+key+"}",customParameter.get(key)); }return str; }/*** file工具类*/private static class FileUtil {/*** 创建文件** @param pathNameAndFileName 路径跟文件名* @return File对象*/private static File createFile(String pathNameAndFileName) {File file = new File(pathNameAndFileName); try {//获取父目录File fileParent = file.getParentFile(); if (!fileParent.exists()) {fileParent.mkdirs(); }//创建文件if (!file.exists()) {file.createNewFile(); }} catch (Exception e) {file = null; System.err.println("新建文件操作出错"); e.printStackTrace(); }return file; }/*** 字符流写入文件** @param filefile对象* @param stringBuffer 要写入的数据*/private static void fileWriter(File file, StringBuffer stringBuffer) {//字符流try {FileWriter resultFile = new FileWriter(file, false); //true,则追加写入 false,则覆盖写入PrintWriter myFile = new PrintWriter(resultFile); //写入myFile.println(stringBuffer.toString()); myFile.close(); resultFile.close(); } catch (Exception e) {System.err.println("写入操作出错"); e.printStackTrace(); }}}/*** 字符串处理工具类*/private static class StringUtil {/*** 数据库类型->JAVA类型** @param dbType 数据库类型* @return JAVA类型*/private static String typeMapping(String dbType) {String javaType; if ("int|integer".contains(dbType)) {javaType = "Integer"; } else if ("float|double|decimal|real".contains(dbType)) {javaType = "Double"; } else if ("date|time|datetime|timestamp".contains(dbType)) {javaType = "Date"; } else {javaType = "String"; }return javaType; }/*** 驼峰转换为下划线*/private static String underscoreName(String camelCaseName) {StringBuilder result = new StringBuilder(); if (camelCaseName != null && camelCaseName.length() > 0) {result.append(camelCaseName.substring(0, 1).toLowerCase()); for (int i = 1; i < camelCaseName.length(); i++) {char ch = camelCaseName.charAt(i); if (Character.isUpperCase(ch)) {result.append("_"); result.append(Character.toLowerCase(ch)); } else {result.append(ch); }}}return result.toString(); }/*** 首字母大写*/private static String captureName(String name) {char[] cs = name.toCharArray(); cs[0] -= 32; return String.valueOf(cs); }/*** 下划线转换为驼峰*/private static String camelCaseName(String underscoreName) {StringBuilder result = new StringBuilder(); if (underscoreName != null && underscoreName.length() > 0) {boolean flag = false; for (int i = 0; i < underscoreName.length(); i++) {char ch = underscoreName.charAt(i); if ("_".charAt(0) == ch) {flag = true; } else {if (flag) {result.append(Character.toUpperCase(ch)); flag = false; } else {result.append(ch); }}}}return result.toString(); }}/*** JDBC连接数据库工具类*/private static class DBConnectionUtil {static {// 1、加载驱动try {Class.forName(DRIVER_CLASSNAME); } catch (ClassNotFoundException e) {e.printStackTrace(); }}/*** 返回一个Connection连接*/static Connection getConnection() {Connection conn = null; // 2、连接数据库try {conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); } catch (SQLException e) {e.printStackTrace(); }return conn; }/*** 关闭Connection,Statement连接*/public static void close(Connection conn, Statement stmt) {try {conn.close(); stmt.close(); } catch (SQLException e) {e.printStackTrace(); }}/*** 关闭Connection,Statement,ResultSet连接*/public static void close(Connection conn, Statement stmt, ResultSet rs) {try {close(conn, stmt); rs.close(); } catch (SQLException e) {e.printStackTrace(); }}}/*** 表结构信息实体类*/private class TableInfo {private String columnName; //字段名private String dataType; //字段类型private String columnComment; //字段注释private String columnKey; //主键private String extra; //主键类型public String getColumnName() {return columnName; }public void setColumnName(String columnName) {this.columnName = columnName; }public String getDataType() {return dataType; }public void setDataType(String dataType) {this.dataType = dataType; }public String getColumnComment() {return columnComment; }public void setColumnComment(String columnComment) {this.columnComment = columnComment; }public String getColumnKey() {return columnKey; }public void setColumnKey(String columnKey) {this.columnKey = columnKey; }public String getExtra() {return extra; }public void setExtra(String extra) {this.extra = extra; }}/*** 获取表结构信息* 目前仅支持mysql*/private List getTableInfo() {Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; ArrayList list = new ArrayList<>(); try {conn = DBConnectionUtil.getConnection(); //表字段信息String sql = "select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_schema = (select database()) and table_name=?"; ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) {TableInfo tableInfo = new TableInfo(); //列名,全部转为小写tableInfo.setColumnName(rs.getString("column_name").toLowerCase()); //列类型tableInfo.setDataType(rs.getString("data_type")); //列注释tableInfo.setColumnComment(rs.getString("column_comment")); //主键tableInfo.setColumnKey(rs.getString("column_key")); //主键类型tableInfo.setExtra(rs.getString("extra")); list.add(tableInfo); }//表注释sql = "select table_comment from information_schema.tables where table_schema = (select database()) and table_name=?"; ps = conn.prepareStatement(sql); ps.setString(1, tableName); rs = ps.executeQuery(); while (rs.next()) {//表注释tableComment = rs.getString("table_comment"); }} catch (SQLException e) {e.printStackTrace(); } finally {if(rs != null){DBConnectionUtil.close(conn, ps, rs); }}return list; }/*** 快速创建,供外部调用,调用之前先设置一下项目的基础路径*/private String create() {System.out.println("生成路径位置:" + filePath); //获取表信息List tableInfo = getTableInfo(); //驼峰标识映射后的表名String captureName = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //自定义参数HashMap customParameter = new HashMap<>(); customParameter.put("author","作者:Auto Generator By 'huanzi-qch'"); customParameter.put("date","生成日期:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); //读取模板、生成代码writer(tlfPath+"controller.tlf",filePath + "controller\\" + captureName + "Controller.java",tableInfo,customParameter); writer(tlfPath+"entity.tlf",filePath + "pojo\\" + captureName + ".java",tableInfo,customParameter); writer(tlfPath+"entityvo.tlf",filePath + "vo\\" + captureName + "Vo.java",tableInfo,customParameter); writer(tlfPath+"repository.tlf",filePath + "repository\\" + captureName + "Repository.java",tableInfo,customParameter); writer(tlfPath+"service.tlf",filePath + "service\\" + captureName + "Service.java",tableInfo,customParameter); writer(tlfPath+"serviceimpl.tlf",filePath + "service\\" + captureName + "ServiceImpl.java",tableInfo,customParameter); return tableName + " 后台代码生成完毕!"; }public static void main(String[] args) {//String[] tables = {"sys_user","sys_menu","sys_authority","sys_user_menu","sys_user_authority","sys_shortcut_menu","sys_setting"}; String[] tables = {"tb_user"}; for (String table : tables) {String msg = new AutoGeneratorPlus(table).create(); System.out.println(msg); }}}
同时,所有代码都在base admin项目里,代码已经开源、托管到我的GitHub、码云:
GitHub:https://github.com/huanzi-qch/base-admin
码云:https://gitee.com/huanzi-qch/base-admin
【教你用Java实现一个简单的代码生成器】到此这篇关于教你用Java实现一个简单的代码生成器的文章就介绍到这了,更多相关Java代码生成器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 慢慢的美丽
- 遇到一哭二闹三打滚的孩子,怎么办┃山伯教育
- 2.6|2.6 Photoshop操作步骤的撤消和重做 [Ps教程]
- “成长”读书社群招募
- 石头巷;名垂青史的廉政教材
- 每日一话(49)——一位清华教授在朋友圈给大学生的9条建议
- 事件代理
- 历史教学书籍
- Java|Java OpenCV图像处理之SIFT角点检测详解