手把手教你从数据库生成实体类(四)

根据上面获取的数据开始创建java文件

终于开始要创建java文件了。

但是~在创建java文件的时候要先吧之前获取的数稍微处理一下,将sql中的格式转换为java中的格式。比如属性名称,数据类型,class名称之类的,现在开始~

将表名称转换为合适的class名称

就是首字母大写,驼峰式的命名规范。例如将user_log或者USER_LOG转换为UserLog。

我们可以这么写:

 /** * 类名称转换 * * @param tableName * @return */ public static String entityName(String tableName) { String lowerCaseName = tableName.toLowerCase(); StringBuilder newName = new StringBuilder(); char[] chars = lowerCaseName.toCharArray(); boolean change = false; for (int i = 0; i < chars.length; i++) { char aChar = chars[i]; if (aChar == ‘_‘ && !change) { change = true; continue; } //首字母大写 if (i == 0) { aChar = Character.toUpperCase(aChar); } if (change) { aChar = Character.toUpperCase(aChar); change = false; } newName.append(aChar); } return newName.toString(); }

这样就得到了我们需要的class的名称了。

将字段名称转换为java中的属性名称

这里就是将上一步操作的首字母大写去掉就好了,下面是代码:

 /** * 属性名称转换 * * @param name * @return */ public static String fieldName(String name) { name = name.toLowerCase(); StringBuilder newName = new StringBuilder(); char[] chars = name.toCharArray(); boolean change = false; for (int i = 0; i < chars.length; i++) { char aChar = chars[i]; if (aChar == ‘_‘ && !change) { change = true; continue; } if (change) { aChar = Character.toUpperCase(aChar); change = false; } newName.append(aChar); } return newName.toString(); }

接下来是将sql中的数据类型转换为java中的数据类型。

sql数据类型转换

这里用map做了一个映射,有自己特定要求的可以自己修改。

public class ColumnFieldTypeMapping { private Map<String, Class> sqlFieldTypeMapping = new HashMap<>(); { sqlFieldTypeMapping.put("VARCHAR", String.class); sqlFieldTypeMapping.put("CHAR", String.class); sqlFieldTypeMapping.put("TEXT", String.class); sqlFieldTypeMapping.put("MEDIUMTEXT", String.class); sqlFieldTypeMapping.put("LONGTEXT", String.class); sqlFieldTypeMapping.put("TINYTEXT", String.class); sqlFieldTypeMapping.put("BIT", Boolean.class); sqlFieldTypeMapping.put("INT", int.class); sqlFieldTypeMapping.put("BIGINT", long.class); sqlFieldTypeMapping.put("DOUBLE", double.class); sqlFieldTypeMapping.put("TINYINT", int.class); sqlFieldTypeMapping.put("FLOAT", float.class); sqlFieldTypeMapping.put("DECIMAL", BigDecimal.class); sqlFieldTypeMapping.put("INT UNSIGNED", int.class); sqlFieldTypeMapping.put("BIGINT UNSIGNED", int.class); sqlFieldTypeMapping.put("DECIMAL UNSIGNED", BigDecimal.class); sqlFieldTypeMapping.put("DATETIME", Date.class); sqlFieldTypeMapping.put("TIME", Date.class); sqlFieldTypeMapping.put("DATE", Date.class); sqlFieldTypeMapping.put("TIMESTAMP", Date.class); } /** * 根据sql数据类型获取Java数据类型 * * @param columnType * @return */ public Class getFieldType(String columnType) { Class aClass = sqlFieldTypeMapping.get(columnType); if (aClass == null) { return sqlFieldTypeMapping.get(columnType.toUpperCase()); } return null; }}

写到这里,所有参与生成java文件的信息就已经获取完成了。

这时候我们需要把他们组装起来,用来放进freemarker中来解析并生成java文件中的内容。

组装参数

这里可能我以后用这个代码干别的事情所以我建了两个类,一个是ClassModel.java,一个是EntityModel.java

EntityModel继承了ClassModel。我们主要用的是EntityModel.java。下面是代码:

import java.util.*;/** * 用于生成java Entity文件的类 */public class ClassModel { /** * java 中不需要引包的类型 */ private static List<Class> baseClass = Arrays.asList( int.class, double.class, float.class, long.class, short.class, byte.class, char.class, boolean.class, String.class ); /** * 类注释 */ private String classDoc; /** * 类名 */ private String className; /** * 类 包名 */ private String packageName; /** * K:属性名称 * V:属性类型 */ private Map<String, Class> fields = new HashMap<>(); /** * 属性的注释 */ private Map<String, String> fieldDoc = new HashMap<>(); ; private List<Class> imports = new ArrayList<>(); /** * 添加需要导入的包 * * @param importClass */ public void addImport(Class importClass) { if (baseClass.indexOf(importClass) != -1) { return; } if (imports.indexOf(importClass) == -1) { imports.add(importClass); } } /** * 添加属性 * * @param fieldName 属性名称 * @param fieldClass 属性类型 */ public void addfield(String fieldName, Class fieldClass) { if (!fields.containsKey(fieldName)) { fields.put(fieldName, fieldClass); } } /** * 添加属性注释 * * @param fieldName 属性名称 * @param fieldDoc 属性注释 */ public void addfieldDoc(String fieldName, String fieldDoc) { if (!this.fieldDoc.containsKey(fieldName)) { this.fieldDoc.put(fieldName, fieldDoc); } } public List<Class> getImports() { return imports; } public void setImports(List<Class> imports) { this.imports = imports; } public String getClassDoc() { return classDoc; } public void setClassDoc(String classDoc) { this.classDoc = classDoc; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public Map<String, Class> getFields() { return fields; } public void setFields(Map<String, Class> fields) { this.fields = fields; } public Map<String, String> getFieldDoc() { return fieldDoc; } public void setFieldDoc(Map<String, String> fieldDoc) { this.fieldDoc = fieldDoc; } @Override public String toString() { final StringBuilder sb = new StringBuilder("{"); sb.append(" \"classDoc\"=\"").append(classDoc).append(‘\"‘); sb.append(", \"className\"=\"").append(className).append(‘\"‘); sb.append(", \"packageName\"=\"").append(packageName).append(‘\"‘); sb.append(", \"fields\"=").append(fields); sb.append(", \"fieldDoc\"=").append(fieldDoc); sb.append(", \"imports\"=").append(imports); sb.append(‘}‘); return sb.toString(); }}
import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 数据库映射 */public class EntityModel extends ClassModel { /** * 数据库名称 */ private String tableName; /** * 数据库中的Id字段名称 */ private List<String> idColumnNames = new ArrayList<>(); /** * 类属性名对应数据库字段映射 * key: class 属性名称 * value:数据库字段名 */ private Map<String, String> fieldSqlName = new HashMap<>(); /** * 添加class 属性映射和 数据库 字段映射 * * @param fieldName * @param sqlName */ public void addfieldSqlName(String fieldName, String sqlName) { if (!fieldSqlName.containsKey(fieldName)) { fieldSqlName.put(fieldName, sqlName); } } /** * 添加id字段名 * * @param idColumnName */ public void addIdColumnName(String idColumnName) { idColumnNames.add(idColumnName); } public String getTableName() { return tableName; } public void setTableName(String tableName) { this.tableName = tableName; } public Map<String, String> getFieldSqlName() { return fieldSqlName; } public void setFieldSqlName(Map<String, String> fieldSqlName) { this.fieldSqlName = fieldSqlName; } public List<String> getIdColumnNames() { return idColumnNames; } public void setIdColumnNames(List<String> idColumnNames) { this.idColumnNames = idColumnNames; }}

在这里将从数据库中得到的数据都组装好,就可以使用freemarker来生成Java文件的内容了。下面是代码:

/** * 根据建表语句组装EntityModel * * @param createTableSql * @return */EntityModel makeModelBySql(String createTableSql) { Formatter formatter = new Formatter(); EntityModel model = new EntityModel(); String tableComment = SqlUtils.getTableComment(createTableSql); String tableName = SqlUtils.getTableName(createTableSql); String id = SqlUtils.getId(createTableSql); model.addIdColumnName(id); model.setClassName(NameConvert.entityName(tableName)); model.setTableName(tableName); //注释是null的时候用数据库表名作为注释 model.setClassDoc(tableComment == null ? tableName : tableComment); List<String> line = SqlUtils.getColumnSqls(createTableSql); for (String oneLine : line) { String columnName = SqlUtils.getByPattern(oneLine, "`(.*)`", 1); String comment = SqlUtils.getByPattern(oneLine, "COMMENT ‘(.*)‘", 1); String columnType = SqlUtils.getByPattern(oneLine, "`" + columnName + "` ([A-Za-z]*)", 1); String fieldName = NameConvert.fieldName(columnName); Class fieldClass = columnFieldTypeMapping.getFieldType(columnType); if (fieldClass == null) { formatter.format("table:%s columnName:%s sql类型:%s 没有映射类型", tableName, columnName, columnType); throw new UnsupportedOperationException(formatter.toString()); } model.addfield(fieldName, fieldClass); //字段注释是null的时候用数据库字段名作为注释 model.addfieldDoc(fieldName, comment == null ? columnName : comment); model.addfieldSqlName(fieldName, columnName); model.addImport(fieldClass); } return model;}

这样一个我们需要的参数就组装好了。现在开始编写freemarker用的代码。

freemarker工具类

用来加载freemarker模板和处理模板中的参数。FreeMarkerUtils.java,代码如下:

import freemarker.cache.StringTemplateLoader;import freemarker.template.Configuration;import freemarker.template.DefaultObjectWrapper;import freemarker.template.Template;import java.io.StringWriter;import java.io.Writer;import java.util.Locale;import java.util.Scanner;public class FreeMarkerUtils { /** * freemarker工具, * * @param subjectParams * @param templetPath * @return * @throws Exception */ public static String getJavaClass(Object subjectParams, String templetPath) throws Exception { StringTemplateLoader loader = new StringTemplateLoader(); Scanner scanner = new Scanner(Thread.currentThread().getContextClassLoader().getResourceAsStream(templetPath)); StringBuilder builder = new StringBuilder(); while (scanner.hasNext()) { builder.append(scanner.nextLine()).append("\n"); } String name = System.currentTimeMillis() + ""; loader.putTemplate(name, builder.toString()); //第一步:实例化Freemarker的配置类 Configuration conf = new Configuration(); conf.setObjectWrapper(new DefaultObjectWrapper()); conf.setLocale(Locale.CHINA); conf.setDefaultEncoding("utf-8"); conf.setTemplateLoader(loader); //处理空值为空字符串 conf.setClassicCompatible(true); Template template = conf.getTemplate(name); Writer out = new StringWriter(2048); template.process(subjectParams, out); String javaClass = out.toString(); return javaClass; }}

现在有了工具类之后,还不能立即开始生成java文件,因为还要继续设置java的package和生成文件的路径,这时候我们可以修改之前写的config.xml

修改config.xml

<xml> <jdbc.url></jdbc.url> <jdbc.username></jdbc.username> <jdbc.password></jdbc.password> <basePath>/home/hjx/work/demo/src/main/java</basePath> <entityPackage>top.hejiaxuan.demo.entity</entityPackage></xml>

这里添加了两个参数:basePathentityPackage。一个是要生成java的文件的路径,一个是java文件的包名。

然后我们再写一个写出文件的工具类FileUtils.java

编写FileUtils.java

import java.io.*;public class FileUtils { /** * 写入文件 * * @param path 文件路径 * @param content 文件内容 */ public static void write(String path, String content) { File file = new File(path); File parentFile = file.getParentFile(); try { if (!parentFile.exists()) { parentFile.mkdirs(); } if (!file.exists()) { file.createNewFile(); } FileWriter fileWriter = new FileWriter(file); fileWriter.write(content); fileWriter.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}

这样就万事具备,就差生成文件啦。下面就开始啦~~~

开始生成java文件

在生成文件前,我们还需要把basePathentityPackage从配置文件里取出来,这一步我就不写了~~

static final String DOT = ".";static final String FILE_TYPE = ".java";static final String ENTITY_TEMPLET_PATH = "EntityTemp.ftl";/** * 用于生成一个类文件 * * @param entityModel * @return */boolean makeOneClass(EntityModel entityModel) { entityModel.setPackageName(entityPackage); String filePath = basePath + "/" + entityPackage.replace(DOT, "/") + "/" + entityModel.getClassName() + FILE_TYPE; try { String javaClassString = FreeMarkerUtils.getJavaClass(entityModel, ENTITY_TEMPLET_PATH); FileUtils.write(filePath, javaClassString); return true; } catch (Exception e) { e.printStackTrace(); } return false;}

好啦~~~大功告成。

额~~~

好像少点啥~~~

模板文件没有放出来~~~

编写EntityTemp.ftl

package ${packageName};<#--导入的包--><#list imports as import>import ${import.name};</#list><#--类名--><#if classDoc?length gt 0>/** * ${classDoc} * @author hejiaxuan */</#if>public class ${className} {<#--属性名称--><#list fields?keys as key> <#assign fieldDocStr = fieldDoc[key]> <#if fieldDocStr?length gt 0> /**${fieldDocStr}*/ </#if> <#if idColumnNames?seq_contains(fieldSqlName[key])> </#if> private ${fields[key].simpleName} ${key};</#list><#list fields?keys as key> <#assign fieldClass = fields[key].simpleName><#--setter--> public void set${key?cap_first}(${fieldClass} ${key}) { this.${key} = ${key}; }<#--getter--> public ${fieldClass} <#if fieldClass="boolean">is<#else>get</#if>${key?cap_first}() { return this.${key}; }</#list> @Override public String toString() { final StringBuilder sb = new StringBuilder("[");<#list fields?keys as key> sb.append("${key}:").append(${key}).append("; ");</#list> sb.append("]"); return sb.toString(); }}

最后

? 这里面我只是贴出来了一些要用到的代码片段,没有将所有的代码全部写出来。其实写工具就是一个慢慢实现自己思路的过程,有思路的话一切都很简单。

? 如果有人需要项目全部代码的话请到https://github.com/hjx601496320/entityMaker自行查看。

相关文章