前言
逆向工程从数据库表直接生成代码,是日常开发中常用的敏捷开发手段,常见的例如: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查询表注释、表字段信息使用的是
表字段信息
SELECT column_name, data_type, column_comment, column_key, extra FROM information_schema.COLUMNS WHERE table_schema = ( SELECT DATABASE ()) AND table_name =?
表注释
SELECT table_comment FROM information_schema.TABLES WHERE table_schema = ( SELECT DATABASE ()) AND table_name =?
需要支持其他数据库类型的,自己调整就好了,例如oracle获取表注释、表结构sql如下:
-- 表、表注释 SELECT t.table_name, t1.comments FROM user_tables t JOIN user_tab_comments t1 ON t.table_name = t1.table_name; -- 表字段、字段注释 SELECT t.table_name, c.column_name, c.data_type, cc.comments FROM USER_TAB_COLUMNS c JOIN user_tables t ON c.table_name = t.table_name JOIN user_col_comments cc ON cc.table_name = t.table_name WHERE cc.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(List<TableInfo> tableInfos) { //创建文件 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); //获取表信息 List<TableInfo> tableInfo = 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}") @Data public class ${entity} implements Serializable { #for #ifPri #ifAutoIncrement private ${tableInfo.dataType} ${tableInfo.columnName};//${tableInfo.columnComment} #end }
${},用于取参数,替换成我们的值
#for、#if,循环遍历表字段以及判断是否为主键、是否主键自增
各种关键字随便定义,我们在读取模板文件处理时能对上就行
文件内容处理
/** * 读取模板,设置内容,生成文件 * @param templatePath 模板文件路径 * @param outputFile 文件生成路径 * @param tableInfos 表字段信息 * @param customParameter 自定义参数 */ private void writer(String templatePath, String outputFile,List<TableInfo> tableInfos,Map<String,String> 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_user if(line.contains("${tableName}")){ line = line.replaceAll("\\$\\{tableName}", tableName); } //${tableComment} 表注释,例如:tb_user if(line.contains("${tableComment}")){ line = line.replaceAll("\\$\\{tableComment}", tableComment); } //${entity} 实体类名称,例如:TbUser if(line.contains("${entity}")){ line = line.replaceAll("\\$\\{entity}", replacement); } //${entityFirstToLowerCase} 实体类名称首字母小写,例如:tbUser if(line.contains("${entityFirstToLowerCase}")){ line = line.replaceAll("\\$\\{entityFirstToLowerCase}", StringUtil.camelCaseName(tableName)); } //${entityToLowerCase} 实体类名称全小写,例如:tbuser if(line.contains("${entityToLowerCase}")){ line = line.replaceAll("\\$\\{entityToLowerCase}", replacement.toLowerCase()); } //${priDataType} 实体类主键类型,例如:String if(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"; //自增id if ("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<String,String> customParameter,例如模板文件中的注释:
/** * ${author} * ${date} */
入口函数
/** * 快速创建,供外部调用,调用之前先设置一下项目的基础路径 */ private String create() { System.out.println("生成路径位置:" + filePath); //获取表信息 List<TableInfo> tableInfo = getTableInfo(); //驼峰标识映射后的表名 String captureName = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //自定义参数 HashMap<String, String> 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.0 AutoGenerator
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(List<TableInfo> tableInfos) { //创建文件 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<TableInfo> 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<TableInfo> 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<TableInfo> 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); //Impl File 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<TableInfo> 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 file file对象 * @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<TableInfo> getTableInfo() { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; ArrayList<TableInfo> 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> 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.0 AutoGeneratorPlus
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,List<TableInfo> tableInfos,Map<String,String> 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_user if(line.contains("${tableName}")){ line = line.replaceAll("\\$\\{tableName}", tableName); } //${tableComment} 表注释,例如:tb_user if(line.contains("${tableComment}")){ line = line.replaceAll("\\$\\{tableComment}", tableComment); } //${entity} 实体类名称,例如:TbUser if(line.contains("${entity}")){ line = line.replaceAll("\\$\\{entity}", replacement); } //${entityFirstToLowerCase} 实体类名称首字母小写,例如:tbUser if(line.contains("${entityFirstToLowerCase}")){ line = line.replaceAll("\\$\\{entityFirstToLowerCase}", StringUtil.camelCaseName(tableName)); } //${entityToLowerCase} 实体类名称全小写,例如:tbuser if(line.contains("${entityToLowerCase}")){ line = line.replaceAll("\\$\\{entityToLowerCase}", replacement.toLowerCase()); } //${priDataType} 实体类主键类型,例如:String if(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"; //自增id if ("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<TableInfo> tableInfos){ writer(templatePath,outputFile,tableInfos,new HashMap<>()); } /** * 处理自定义参数 */ private String customParameter(String str,Map<String,String> 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 file file对象 * @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<TableInfo> getTableInfo() { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; ArrayList<TableInfo> 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> tableInfo = getTableInfo(); //驼峰标识映射后的表名 String captureName = StringUtil.captureName(StringUtil.camelCaseName(tableName)); //自定义参数 HashMap<String, String> 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