寫一個簡單的代碼生成器
前言
逆向工程從數據庫表直接生成代碼,是日常開發中常用的敏捷開發手段,常見的例如: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); } } }
View Code
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); } } }
View Code
同時,所有代碼都在base admin項目里,代碼已經開源、託管到我的GitHub、碼云:
GitHub://github.com/huanzi-qch/base-admin
碼云://gitee.com/huanzi-qch/base-admin