代碼生成器(記錄一次興趣代碼,多多指教。轉載請標明做者)java
在咱們開始實現代碼生成器以前咱們先來對代碼生成器有一個簡單的瞭解。mysql
1.什麼是代碼生成器?程序員
故名思義,也就是生成代碼的一個程序。那它是一個什麼樣的機制?怎麼體現這種機制?sql
好比:
咱們學習的 JSP 技術,瀏覽器並不能直接識別 JSP 文件。當用戶經過瀏覽器訪問某個 JSP 頁
面的時候,其中的交互分爲分爲幾個步驟:
用戶經過瀏覽器訪問 JSP 頁面
Tomcat 找到對應 JSP 資源,將 JSP 資源轉化(編譯,翻譯)成爲 HTML 數據
Tomcat 將 HTML 數據進行返回
在此,咱們就能夠這樣理解
咱們所編寫 JSP ===》 HTML 的模板。
JSP 頁面的標籤 ===》模板中的佔位符。
Tomcat 根據 JSP 模板,生成 HTML 頁面。 數據庫的數據不一樣,則生成的 HTML 頁面也不一樣。
最後咱們得出一個結論:
Tomcat 能夠根據 JSP 模板和數據生成不一樣的 HTML 頁面。數據庫
因此 Tomcat 對 JSP 的處理過程咱們就能夠認爲是一個代碼生成器的機制,其實也就是代碼生成器的原理。瀏覽器
2.咱們爲何要編寫代碼生成器?app
咱們先不說它在咱們軟件之中到處可見,單單就做爲一個程序員的輔助工具而言,也是極大的提升了開發效率。maven
好比咱們的實體類,一個項目中表的數量不會少於二三十個吧,假若每個實體類,都手動編寫,這種重複的代碼編寫,嗯,做爲程序員應該都懂得。ide
因此,代碼生成器節省人力成本,提升了開發效率。工具
3.開始編寫代碼生成器。
前面鋪墊了那麼多,乾貨終於要來了。
3.1 編寫工具:idea
3.2 依賴模板引擎: FreeMarker(FreeMarker 所須要的功能簡介:請點擊個人另外一篇博客 FreeMarker)
maven依賴freemarker jar
<!-- https://mvnrepository.com/artifact/freemarker/freemarker --> <dependency> <groupId>freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.8</version> </dependency>
3.3 代碼體系組成:
3.4 全部實現代碼
---------存放數據的對象
package cn.itrip.vo; /** * 列對象 */ public class ColumnVo { private String name;//數據庫中的列名 private String fieldName;//對應的Java屬性名 private String dbType;//數據中的記錄類型 private String javaType;//對應的Java屬性類型 private String comment;//數據庫中的註釋 private String upperCaseColumnName;//轉成帕斯卡後的屬性名 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFieldName() { return fieldName; } public void setFieldName(String fieldName) { this.fieldName = fieldName; } public String getDbType() { return dbType; } public void setDbType(String dbType) { this.dbType = dbType; } public String getJavaType() { return javaType; } public void setJavaType(String javaType) { this.javaType = javaType; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public String getUpperCaseColumnName() { return upperCaseColumnName; } public void setUpperCaseColumnName(String upperCaseColumnName) { this.upperCaseColumnName = upperCaseColumnName; } }
package cn.itrip.vo; import java.util.ArrayList; import java.util.List; /** * 表對象 */ public class TableVo { private String className;//帕斯卡風格的類名 private String tableName;//表名 private String comment;//註釋 private String lowerClassName;//駱駝命名法 private List<ColumnVo> columns=new ArrayList<ColumnVo>();//列對象集合 public String getLowerClassName() { return lowerClassName; } public void setLowerClassName(String lowerClassName) { this.lowerClassName = lowerClassName; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getTableName() { return tableName; } public void setTableName(String tableName) { this.tableName = tableName; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public List<ColumnVo> getColumns() { return columns; } public void setColumns(List<ColumnVo> columns) { this.columns = columns; } }
--------------幫助類
package cn.itrip.util; /** * 命名轉換工具類 */ public class JavaNameUtil { public static String translate(String underscoreName,boolean isPascal){ StringBuilder result = new StringBuilder();//返回字符結果 if(underscoreName!=null && underscoreName.length()>0){ boolean flag=false; char firstChar =underscoreName.charAt(0);//判斷獲取首字母 if(isPascal){ result.append(Character.toUpperCase(firstChar));//將首字母轉換成大寫 }else{ result.append(firstChar); } //從第二個字符開始循環 for (int i=1;i<underscoreName.length();i++){ char ch=underscoreName.charAt(i); //判斷是否出現下劃線 if('_'==ch){ flag=true; }else { if(flag){ result.append(Character.toUpperCase(ch)); flag=false; }else { result.append(ch); } } } } return result.toString();//返回轉換字符 } /** * 駱駝命名法 * @param str * @return */ public static String toCamel(String str){ return translate(str,false); } /** * 帕斯卡命名法 * @param str * @return */ public static String toPascal(String str){ return translate(str,true); } /** * 轉換數據類型 * @param dbTypeName * @return */ public static String dbtype2JavaType(String dbTypeName){ String javaType=null; switch (dbTypeName){ case "VARCHAR":javaType="String";break; case "BIGINT":javaType="Long";break; case "INT":javaType="Integer";break; case "DATETIME":javaType="Date";break; default: javaType="String";break; } return javaType; } public static void main(String[] arg){ String testName =toCamel("user_liu_jh"); System.out.println(testName); String testName2=toPascal("user_liu_jh"); System.out.println(testName2); } }
package cn.itrip.util; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * 負責鏈接數據庫、獲取表的元信息、列的元信息 */ public class MetadataUtil { private static Connection conn;//鏈接對象 /*2. DatabaseMetaData接口經常使用的方法:獲取數據庫元數據 (1) ResultSet getTables(String catalog,String schemaPattern,String tableNamePattern,String[] types); //獲取表信息 (2) ResultSet getPrimaryKeys(String catalog,String schema,String table); //獲取表主鍵信息 (3) ResultSet getIndexInfo(String catalog,String schema,String table,boolean unique,boolean approximate); //獲取表索引信息 (4) ResultSet getColumns(String catalog,String schemaPattern,String tableNamePattern,String columnNamePattern); //獲取表列信息*/ private static DatabaseMetaData meta; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); System.out.println("數據庫鏈接失敗!"); } } /** * 鏈接數據庫獲取數據庫元數據 */ public static void openConnection() { try { if (conn == null || conn.isClosed()) {//isClosed:判斷鏈接是否關閉 conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/XXX?useUnicode=true&characterEncoding=utf-8" , "XXX", "XXX");//鏈接數據庫 XXX表示你本身的數據庫名稱 、用戶名、密碼 meta = conn.getMetaData();//獲取元數據 } } catch (SQLException e) { e.printStackTrace(); } } /** * 獲取表的註釋 * @param * @return */ public static String getCommentByTableName(String tableName) throws SQLException { openConnection();//打開鏈接 Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SHOW CREATE TABLE " + tableName); String comment = null; if (rs != null && rs.next()) { comment=rs.getString(2); //判斷字符,只獲取註解部分 int index = comment.lastIndexOf("="); comment = comment.substring(index+1); } rs.close(); stmt.close(); conn.close(); return comment; } /** * 獲取全部的表名 * @return */ public static String[] getTableNames(){ openConnection(); ResultSet rs=null; List<String> nameList = new ArrayList<>(); try{ rs=meta.getTables(null, null, null, new String[]{"TABLE"}); while (rs.next()){ String tName =rs.getString("TABLE_NAME"); nameList.add(tName);//將取出來的表名放入集合中 } }catch (SQLException e){ e.printStackTrace(); } return (String[])nameList.toArray(new String[]{}); } /** * 獲取某個表中全部的列信息 * @param tableName * @return * @throws Exception */ public static List<String[]> getTableColumnsInfo(String tableName)throws Exception{ openConnection(); ResultSet rs= meta.getColumns(null, "%",tableName,"%"); List<String[]> columnInfoList =new ArrayList<>(); while (rs.next()){ String[] colInfo = new String[3]; colInfo[0]=rs.getString("COLUMN_NAME");//COLUMN_NAME 列名 colInfo[1]=rs.getString("REMARKS");//REMARKS 獲取列註釋 colInfo[2]=rs.getString("TYPE_NAME");//TYPE_NAME 列類型 columnInfoList.add(colInfo); } return columnInfoList; } }
---------合成機制
package cn.itrip.generator; import cn.itrip.util.JavaNameUtil; import cn.itrip.util.MetadataUtil; import cn.itrip.vo.ColumnVo; import cn.itrip.vo.TableVo; import freemarker.template.Configuration; import freemarker.template.Template; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 抽象生成器 */ public abstract class AbstractGenerator { protected Configuration cfg;//Freemarker配置對象 protected Map valueMap;//填充到模板的數據 protected Template template;//模板對象 protected String savePath;//保存生成文件的路徑 protected List<TableVo> tableList;//表信息 public AbstractGenerator() throws Exception { cfg = new Configuration(); cfg.setClassForTemplateLoading(this.getClass(), "/templates/"); //獲取模板文件位置 valueMap = new HashMap(); init();//初始化數據 } /** * 獲取全部的表信息列信息 * @throws Exception */ public void init()throws Exception{ tableList=new ArrayList<>(); String[] tableNameArr =MetadataUtil.getTableNames();//獲取表名稱 for(String tName:tableNameArr){ String tComment = MetadataUtil.getCommentByTableName(tName);//獲取註釋 TableVo table = new TableVo();//建立表信息對象 table.setClassName(JavaNameUtil.toPascal(tName)); table.setComment(tComment); table.setTableName(tName); List<String[]> colInfoList =MetadataUtil.getTableColumnsInfo(tName); for (String[] colInfo:colInfoList){ String cName =colInfo[0]; String cComment =colInfo[1]; String cType =colInfo[2]; ColumnVo column = new ColumnVo(); column.setComment(cComment); column.setName(cName); column.setFieldName(JavaNameUtil.toCamel(cName)); column.setDbType(cType); column.setJavaType(JavaNameUtil.dbtype2JavaType(cType)); column.setUpperCaseColumnName(JavaNameUtil.toPascal(cName)); table.getColumns().add(column); } tableList.add(table); System.out.println(table); } System.out.println("構建元數據成功!"); } //抽象生成代碼文件,各個子類實現 public abstract void generateCode() throws Exception; public void setSavePath(String savePath) { this.savePath = savePath; } }
package cn.itrip.generator; import cn.itrip.vo.TableVo; import freemarker.template.TemplateException; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; /** * 實體類生成器 */ public class EntityGenerator extends AbstractGenerator{ public EntityGenerator(String ftl) throws Exception {
try{
this.savePath="D:\\object\\itripljh\\itripbeans\\src\\main\\java\\cn\\itrip\\pojo";
//加載freemarker模板文件
super.template = cfg.getTemplate(ftl);
}catch (IOException e){
e.printStackTrace();
}
} public void generateCode() throws Exception { System.out.println("---------------開始生成代碼"); valueMap.put("package","cn.itrip");//自定義包的位置 OutputStreamWriter writer=null; for(TableVo table:tableList) { valueMap.put("table",table); try{ //建立一個寫入器 writer = new FileWriter(savePath+"/"+table.getClassName()+".java");//根據不一樣的文件自行修改 //合成數據和模板,寫入到文件 this.template.process(valueMap,writer); writer.flush(); }catch (TemplateException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally { writer.close(); } } System.out.println("生成代碼成功!"); } }
----------------模板文件(pojo.ftl)
package ${package}.pojo; import java.io.Serializable; import java.util.Date; /** * ${table.comment} */ public class ${table.className} implements Serializable{ //生成私有屬性 <#list table.columns as col> //${col.comment} private ${col.javaType} ${col.fieldName}; </#list> //生成getter、setter方法 <#list table.columns as col> public void set${col.upperCaseColumnName} (${col.javaType} ${col.fieldName}){ this.${col.fieldName} =${col.fieldName}; } public ${col.javaType} get${col.upperCaseColumnName} (){ return this.${col.fieldName}; } </#list> }
---------------入口
package cn.itrip; import cn.itrip.generator.EntityGenerator;public class App { public static void main(String[] args)throws Exception{ System.out.println("開始啓動---------------"); String path=App.class.getClassLoader().getResource("").getPath(); System.out.println(path); EntityGenerator generator =new EntityGenerator("pojo.ftl"); try { generator.setSavePath("D:\\object\\itripljh\\itripbeans\\src\\main\\java\\pojo2"); generator.generateCode(); }catch (Exception e){ e.printStackTrace(); }
}
-----------最後執行的結果以下
完成。