在javaweb開發過程中往往需要創建很多與數據庫關聯的java實體類與sql(xml格式)文件,這些工作是既耗時,又沒什么技術含量,但是卻是必不可少的工作。使用過mybatis的都知道,mybatis有對應的插件可以快速生成相應的java類與sql文件,通過使用插件可以大大提高開發效率。但是並不是所有的公司都使用mybatis,本人所在的公司就是其中之一。本人所在公司雖然使用的不是mybatis,但是開發過程卻是很像,都需要創建實體類與sql文件。由於每次開發都需要創建大量的實體類與sql文件,使本人開發時感到很痛苦,尤其數據庫表字段特別多的時候真是相當的痛苦。基於以上原因,本人研究了一下使用FreeMarker 生成文件的方法,在此記錄一下,也方便以后的查找。
“FreeMarker是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。模板編寫為FreeMarker Template Language (FTL)。它是簡單的,專用的語言, 不是 像PHP那樣成熟的編程語言。 那就意味着要准備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之后模板顯示已經准備好的數據。在模板中,你可以專注於如何展現數據, 而在模板之外可以專注於要展示什么數據。”--來源於網絡
FreeMarker是一種模板引擎,使用過springmvc的應該都知道,springmvc支持多種模板引擎,其中FreeMarker就是其中之一。當然,在springmvc中主要用來做作為前端頁面使用。要使用FreeMarker,首先要有對應的模板,其模板的格式為*.ftl。行了,說了這么多,好像都是廢話,下面直接上代碼。
1、ftl模板
此處主要有兩個模板:bean.ftl和sqlMap.ftl,一個用來生成實體類,一個用來生成sql文件。
bean.ftl
package ${packageName}; import com.alibaba.fastjson.JSONObject; import java.util.Date; /** * 〈一句話功能簡述〉 * 〈功能詳細描述〉 * * @author 19043197 * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class ${className} implements Serializable { private static final long serialVersionUID = 1L; <#-- 循環類型及屬性 --> <#list attrs as attr> //${attr.remarks} private ${attr.type} ${attr.name}; </#list> <#-- 循環生成set get方法 --> <#list attrs as attr> public void set${attr.firstUpperName}(${attr.type} ${attr.name}) { this.${attr.name} = ${attr.name}; } public ${attr.type} get${attr.firstUpperName}() { return ${attr.name}; } </#list> @Override public String toString() { return JSONObject.toJSONString(this); } }
sqlMap.ftl
package ${packageName}; import com.alibaba.fastjson.JSONObject; import java.util.Date; /** * 〈一句話功能簡述〉 * 〈功能詳細描述〉 * * @author 19043197 * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class ${className} implements Serializable { private static final long serialVersionUID = 1L; <#-- 循環類型及屬性 --> <#list attrs as attr> //${attr.remarks} private ${attr.type} ${attr.name}; </#list> <#-- 循環生成set get方法 --> <#list attrs as attr> public void set${attr.firstUpperName}(${attr.type} ${attr.name}) { this.${attr.name} = ${attr.name}; } public ${attr.type} get${attr.firstUpperName}() { return ${attr.name}; } </#list> @Override public String toString() { return JSONObject.toJSONString(this); } }
簡單介紹下FreeMarker語法:
(1)、${className} 直接獲取對象屬性
(2)、<#list attrs as attr></#list> 對集合進行循環
(3)、<#if attr_has_next></#if> 條件判斷,attr_has_next判斷還有下一個,格式:對象_has_next
(4)、${r" "}或${r''}特殊字符顯示,由於本人所在公司sql(xml格式)文件使用的freemerker解析的,所以生成的文件中難免會有以下Freemarker的標簽
2、數據庫連接類(此處只貼了mysql的)
package utils; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * 數據庫連接工具類(僅mysql) * 〈功能詳細描述〉 * * @author lipan * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class MysqlDBUtil { private static String URL; private static String USER; private static String PASSWORD; private static String DRIVER; static{ URL = "jdbc:mysql://localhost:3306/lpan?useSSL=false&serverTimezone=UTC"; USER = "root"; PASSWORD = "1234"; DRIVER = "com.mysql.cj.jdbc.Driver"; } public static Connection getConnection() throws Exception{ Class.forName(DRIVER); Connection connection= DriverManager.getConnection(URL, USER, PASSWORD); return connection; } public static void close(Connection connection){ if(connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
3、ColumnBean類
主要是定義數據字段與實體類屬性
package utils; /** * 實體類 * 〈功能詳細描述〉 * * @author [作者](必須) * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class ColumnBean { //成員變量類型 private String type; //成員變量名稱 private String name; //注釋 private String remarks; //首字母大寫 private String firstUpperName; //字段(大寫) private String columnName; //指定長度的字段大寫,不足右側補空格(select sql使用,美觀,個人習慣) private String columnNameWithLen; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRemarks() { return remarks; } public void setRemarks(String remarks) { this.remarks = remarks; } public String getFirstUpperName() { return firstUpperName; } public void setFirstUpperName(String firstUpperName) { this.firstUpperName = firstUpperName; } public String getColumnName() { return columnName; } public void setColumnName(String columnName) { this.columnName = columnName; } public String getColumnNameWithLen() { return columnNameWithLen; } public void setColumnNameWithLen(String columnNameWithLen) { this.columnNameWithLen = columnNameWithLen; } public ColumnBean(String type, String name, String remarks, String firstUpperName, String columnName, String columnNameWithLen) { this.type = type; this.name = name; this.remarks = remarks; this.firstUpperName = firstUpperName; this.columnName = columnName; this.columnNameWithLen = columnNameWithLen; } public ColumnBean() { } }
4、從數據庫查詢表字段信息封裝成ColumnBean(此處有mysql和postgresql兩種)
package utils; import com.alibaba.fastjson.JSONObject; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 數據處理 * 〈功能詳細描述〉 * * @author [作者](必須) * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class DataUtils { public static String dealDataTypeForPG(String dataType){ if(dataType==null || dataType.length()==0){ throw new RuntimeException("字段類型為空"); } if(dataType.contains("character varying")){ return "String"; }else if(dataType.contains("numeric")){ return "BigDecimal"; }else if(dataType.contains("timestamp")){ return "Date"; }else if(dataType.contains("int4")){ return "Integer"; } throw new RuntimeException("字段類型轉換異常"); } public static String dealDataTypeForMysql(String dataType){ if(dataType==null || dataType.length()==0){ throw new RuntimeException("字段類型為空"); } if(dataType.contains("varchar")){ return "String"; }else if(dataType.contains("decimal")){ return "BigDecimal"; }else if(dataType.contains("datetime")){ return "Date"; }else if(dataType.contains("int")){ return "Integer"; } throw new RuntimeException("字段類型轉換異常"); } /** * 首字母大寫 * @param columnName * @return */ public static String dealColumnNameFirstUpper(String columnName){ return columnName.substring(0,1)+columnName.substring(1); } private static Pattern linePattern = Pattern.compile("_(\\w)"); /** 下划線轉駝峰 */ public static String lineToHump(String str) { str = str.toLowerCase(); Matcher matcher = linePattern.matcher(str); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); } matcher.appendTail(sb); return sb.toString(); } public static ColumnBean createColumnBeanName(String dataType,String columnName,String remarks,String dbType){ String type = ""; if("mysql".equals(dbType)){ type = dealDataTypeForMysql(dataType); }else{ type = dealDataTypeForPG(dataType); } String name = lineToHump(columnName); String firstUpperName = dealColumnNameFirstUpper(name); String newColumnName = addEmptyChar(columnName.toUpperCase(), 25); ColumnBean columnBean = new ColumnBean(type,name,remarks,firstUpperName,columnName.toUpperCase(),newColumnName); return columnBean; } /** * 字符串不足指定長度補空格 * @param str * @param length * @return */ public static String addEmptyChar(String str,int length){ StringBuffer buffer = new StringBuffer(); if(str.isEmpty()){ for(int i=0;i<length;i++){ buffer.append(" "); } }else if(str.length()<length){ buffer.append(str); for(int i=0;i<length-str.length();i++){ buffer.append(" "); } }else{ buffer.append(str); } return buffer.toString(); } public static List<ColumnBean> processDataForPG(Connection connection, String tableName) throws SQLException { StringBuffer sql = new StringBuffer(); sql.append(" SELECT "); sql.append(" b.attname AS column_name, "); sql.append(" c.description AS remarks, "); sql.append(" pg_catalog.format_type(b.atttypid,b.atttypmod) AS data_type "); sql.append(" FROM "); sql.append(" pg_catalog.pg_class a, "); sql.append(" pg_catalog.pg_attribute b, "); sql.append(" pg_catalog.pg_description c "); sql.append(" WHERE "); sql.append(" a.oid=b.attrelid "); sql.append(" AND b.attrelid=c.objoid "); sql.append(" AND a.relname= ? "); sql.append(" AND c.objsubid=b.attnum "); PreparedStatement preparedStatement = connection.prepareStatement(sql.toString()); preparedStatement.setString(1,tableName); System.out.println(preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); List<ColumnBean> list = new ArrayList<ColumnBean>(); while (resultSet.next()){ String dataType = resultSet.getString("data_type"); String columnName = resultSet.getString("column_name"); String remarks = resultSet.getString("remarks"); ColumnBean columnBean = createColumnBeanName(dataType, columnName, remarks,"PG"); list.add(columnBean); } return list; } public static List<ColumnBean> processDataForMysql(Connection connection, String tableName) throws SQLException { StringBuffer sql = new StringBuffer(); sql.append(" select * from information_schema.COLUMNS where table_name= ? "); PreparedStatement preparedStatement = connection.prepareStatement(sql.toString()); preparedStatement.setString(1,tableName); System.out.println(preparedStatement.toString()); ResultSet resultSet = preparedStatement.executeQuery(); List<ColumnBean> list = new ArrayList<ColumnBean>(); while (resultSet.next()){ String dataType = resultSet.getString("data_type"); String columnName = resultSet.getString("column_name"); String remarks = resultSet.getString("column_comment"); ColumnBean columnBean = createColumnBeanName(dataType, columnName, remarks,"mysql"); list.add(columnBean); } return list; } }
5、使用FreeMarker生成具體文件
package utils; import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.sql.Connection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 模板生成類 * 〈功能詳細描述〉 * * @author [作者](必須) * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class FreemarkerGenerator { //模板路徑 public static String PATH = "D:\\IdeaProjects\\freemarker-generator\\src\\main\\resources"; //生成文件路徑 public static String OUT_PATH = "E:\\study\\"; public static void createBean(Connection connection,String packageName, String className, String tableName,String dbType){ // step1 創建freeMarker配置實例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_0); Writer out = null; try { // step2 獲取模版路徑 configuration.setDirectoryForTemplateLoading(new File(PATH)); // step3 創建數據模型 Map<String, Object> dataMap = new HashMap<String, Object>(); List<ColumnBean> columnBeans = null; if("mysql".equals(dbType)){ columnBeans = DataUtils.processDataForMysql(connection,tableName); }else{ columnBeans = DataUtils.processDataForPG(connection,tableName); } dataMap.put("packageName", packageName); dataMap.put("className", className); dataMap.put("attrs",columnBeans); // step4 加載模版文件 Template template = configuration.getTemplate("bean.ftl"); // step5 生成數據 File docFile = new File(OUT_PATH+className+".java"); out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // step6 輸出文件 template.process(dataMap, out); System.out.println("文件創建成功 !"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } public static void createSqlMap(Connection connection,String tableName,String fileName,String namespace,String dbType){ // step1 創建freeMarker配置實例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_0); Writer out = null; try { // step2 獲取模版路徑 configuration.setDirectoryForTemplateLoading(new File(PATH)); // step3 創建數據模型 Map<String, Object> dataMap = new HashMap<String, Object>(); List<ColumnBean> columnBeans = null; if("mysql".equals(dbType)){ columnBeans = DataUtils.processDataForMysql(connection,tableName); }else{ columnBeans = DataUtils.processDataForPG(connection,tableName); } dataMap.put("tableName", tableName); dataMap.put("namespace", namespace); dataMap.put("pageSize","${pageSize}"); dataMap.put("startPage","${startPage}"); dataMap.put("attrs",columnBeans); // step4 加載模版文件 Template template = configuration.getTemplate("sqlMap.ftl"); // step5 生成數據 File docFile = new File(OUT_PATH+fileName+".xml"); out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // step6 輸出文件 template.process(dataMap, out); System.out.println("文件創建成功 !"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } }
PATH 模板的路徑
OUT_PATH生成文件的路徑
代碼注釋應該比較清楚,在此不多做解釋。
6、測試類
package utils; import com.alibaba.fastjson.JSONObject; import freemarker.template.Configuration; import freemarker.template.Template; import org.junit.Test; import java.io.*; import java.sql.Connection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 測試類 * 〈功能詳細描述〉 * * @author [作者](必須) * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class FreemarkerGeneratorTest { @Test public void testKeyWord() { // step1 創建freeMarker配置實例 Configuration configuration = new Configuration(Configuration.VERSION_2_3_0); Writer out = null; try { // step2 獲取模版路徑 configuration.setDirectoryForTemplateLoading(new File(FreemarkerGenerator.PATH)); // step3 創建數據模型 Map<String, Object> dataMap = new HashMap<String, Object>(); dataMap.put("aa", "${pageSize}"); // step4 加載模版文件 Template template = configuration.getTemplate("test.ftl"); // step5 生成數據 File docFile = new File(FreemarkerGenerator.OUT_PATH + "aa.txt"); out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // step6 輸出文件 template.process(dataMap, out); System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^文件創建成功 !"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } @Test public void processDataForMysqlTest() throws Exception { Connection connection = MysqlDBUtil.getConnection(); List<ColumnBean> list = DataUtils.processDataForMysql(connection, "d_menu"); JSONObject.toJSONString(list); System.out.println(JSONObject.toJSONString(list)); MysqlDBUtil.close(connection); } @Test public void createBeanWithMysql() throws Exception { String packageName = "com.test"; String className = "Menu"; String tableName = "d_menu"; Connection connection = MysqlDBUtil.getConnection(); FreemarkerGenerator.createBean(connection,packageName,className,tableName,"mysql"); PGDBUtil.close(connection); } @Test public void createSqlMapWithMysql() throws Exception { String tableName = "d_menu"; String fileName = "sqlMap_menu"; String namespace = "menu"; Connection connection = MysqlDBUtil.getConnection(); FreemarkerGenerator.createSqlMap(connection,tableName,fileName,namespace,"mysql"); PGDBUtil.close(connection); } }
createBeanWithMysql和createSqlMapWithMysql分別是測試生成實體類和sql配置文件。
生成后的文件
Menu.java
package com.test; import com.alibaba.fastjson.JSONObject; /** * 〈一句話功能簡述〉 * 〈功能詳細描述〉 * * @author xxxxx * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class Menu implements Serializable { private static final long serialVersionUID = -5717628049858495391L; //主鍵 private Integer menuId; //創建時間 private Date createDate; //圖標 private String iconCls; //菜單名稱 private String menuName; //父id private Integer pid; //狀態 private String status; //菜單地址 private String url; public void setmenuId(Integer menuId) { this.menuId = menuId; } public Integer getmenuId() { return menuId; } public void setcreateDate(Date createDate) { this.createDate = createDate; } public Date getcreateDate() { return createDate; } public void seticonCls(String iconCls) { this.iconCls = iconCls; } public String geticonCls() { return iconCls; } public void setmenuName(String menuName) { this.menuName = menuName; } public String getmenuName() { return menuName; } public void setpid(Integer pid) { this.pid = pid; } public Integer getpid() { return pid; } public void setstatus(String status) { this.status = status; } public String getstatus() { return status; } public void seturl(String url) { this.url = url; } public String geturl() { return url; } @Override public String toString() { return JSONObject.toJSONString(this); } }
sqlMap_menu.xml
<?xml version="1.0" encoding="UTF-8" ?> <sqlMap namespace="menu"> <sql id="queryCount"> <![CDATA[ SELECT COUNT(ID) AS TOTALCOUNT FROM D_MENU WHERE 1=1 ]]> </sql> <sql id="queryList"> <![CDATA[ SELECT MENU_ID AS "menuId", CREATE_DATE AS "createDate", ICON_CLS AS "iconCls", MENU_NAME AS "menuName", PID AS "pid", STATUS AS "status", URL AS "url" FROM D_MENU WHERE 1=1 LIMIT ${pageSize} OFFSET ${startPage} ]]> </sql> <sql id="insert"> <![CDATA[ INSERT INTO D_MENU( MENU_ID, CREATE_DATE, ICON_CLS, MENU_NAME, PID, STATUS, URL ) VALUES ( :menuId, :createDate, :iconCls, :menuName, :pid, :status, :url ) ]]> </sql> <sql id="update"> <![CDATA[ UPDATE D_MENU SET MENU_ID = :menuId, CREATE_DATE = :createDate, ICON_CLS = :iconCls, MENU_NAME = :menuName, PID = :pid, STATUS = :status, URL = :url WHERE <#if menuId?exists && menuId!=''> MENU_ID = :menuId </#if> <#if createDate?exists && createDate!=''> AND CREATE_DATE = :createDate </#if> <#if iconCls?exists && iconCls!=''> AND ICON_CLS = :iconCls </#if> <#if menuName?exists && menuName!=''> AND MENU_NAME = :menuName </#if> <#if pid?exists && pid!=''> AND PID = :pid </#if> <#if status?exists && status!=''> AND STATUS = :status </#if> <#if url?exists && url!=''> AND URL = :url </#if> ]]> </sql> </sqlMap>
github地址:https://github.com/panli1988/freemarker-generator
以上全部內容,僅供參考