手把手教你从数据库生成实体类(四)
根据上面获取的数据开始创建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>
这里添加了两个参数:basepath和entitypackage。一个是要生成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文件
在生成文件前,我们还需要把basepath,entitypackage从配置文件里取出来,这一步我就不写了~~
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自行查看。
完
上一篇: 设计模式:策略模式