FreeMarker生成java类与sql文件
在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
以上全部内容,仅供参考