欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-08 21:23:50
根据上面获取的数据开始创建java文件 终于开始要创建java文件了。 但是~在创建java文件的时候要先吧之前获取的数稍微处理一下,将sql中的格式转换为java中的格式。比如属性名称,数据类型,class名称之类的,现在开始~ 将表名称转换为合适的class名称 就是首字母大写,驼峰式的命名规范 ......

根据上面获取的数据开始创建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自行查看。