前言
逆向工程從資料庫表直接生成程式碼,是日常開發中常用的敏捷開發手段,常見的例如: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、碼雲: