手撸一个代码自动生成器!!

实现功能:mybatis 逆向工程

技术架构

页面是用 vue ,element-ui开发;网络请求是 axios。
服务端是 spring boot
页面模版是 freemarker:

开发步骤:

一、创建工程

二、数据库连接操作

1.所需包结构

2.在model包中创建db类

作用:用于接受前端传来数据库连接相关的值(username,password,url)

package com.example.generate_code.model;

/**
 * @author: 王泽
 */

public class db {
    private string username;
    private string password;
    private string url;


    public string getusername() {
        return username;
    }

    public void setusername(string username) {
        this.username = username;
    }

    public string getpassword() {
        return password;
    }

    public void setpassword(string password) {
        this.password = password;
    }

    public string geturl() {
        return url;
    }

    public void seturl(string url) {
        this.url = url;
    }
}

3.在model中创建respbean类

自定义响应类,返回数据更方便

package com.example.generate_code.model;

/**
 * @author: 王泽
 */

public class respbean {
    private integer status;
    private string msg;
    private object obj;

    public static respbean ok(string msg,object obj) {
        return new respbean(200, msg, obj);
    }


    public static respbean ok(string msg) {
        return new respbean(200, msg, null);
    }


    public static respbean error(string msg,object obj) {
        return new respbean(500, msg, obj);
    }


    public static respbean error(string msg) {
        return new respbean(500, msg, null);
    }

    private respbean() {
    }

    private respbean(integer status, string msg, object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }

    public integer getstatus() {
        return status;
    }

    public void setstatus(integer status) {
        this.status = status;
    }

    public string getmsg() {
        return msg;
    }

    public void setmsg(string msg) {
        this.msg = msg;
    }

    public object getobj() {
        return obj;
    }

    public void setobj(object obj) {
        this.obj = obj;
    }


}

4.在utils中创建dbutils

jdbc连接工具类

public class dbutils {
    private static connection connection;

    public static connection getconnection() {
        return connection;
    }

    public static connection initdb(db db) {
        if (connection == null) {
            try {
                class.forname("com.mysql.cj.jdbc.driver");
                connection = drivermanager.getconnection(db.geturl(), db.getusername(), db.getpassword());
            } catch (classnotfoundexception | sqlexception e) {
                e.printstacktrace();
            }
        }
        return connection;
    }


}

5.写一个连接接口dbcontroller

连接数据库

@restcontroller
public class dbcontroller {
    @postmapping("/connect")
    public respbean connect(@requestbody db db) {
        connection con = dbutils.initdb(db);
        if (con != null) {
            return respbean.ok("数据库连接成功");
        }
        return respbean.error("数据库连接失败");
    }
}

6.创建index页面

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>代码生成工具</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
    <table>
        <tr>
            <td>
                <el-tag size="mini">数据库用户名:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.username"></el-input>
            </td>
        </tr>
        <tr>
            <td>
                <el-tag size="mini">数据库密码:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.password"></el-input>
            </td>
        </tr>
        <tr>
            <td>
                <el-tag size="mini">数据库连接地址:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.url">
                    <template slot="prepend">jdbc:mysql://</template>
                    <template slot="append">
                        ?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai
                    </template>
                </el-input>
            </td>
        </tr>
    </table>
    <div style="display: flex">
        <el-button type="primary" size="mini" @click="connect" :disabled="!connectbtnenabled">连接数据库</el-button>
        <div style="color: #ff018d;font-weight: bold">[{{msg}}]</div>
        <el-input v-model="packagename" size="mini" style="width: 300px"></el-input>
        <el-button type="primary" size="mini" @click="config">配置</el-button>
    </div>
</div>
<script>
    new vue({
        el: "#app",
        data: function () {
            return {
                packagename: '',
                msg: '数据库未连接',
                connectbtnenabled: true,
                db: {
                    username: "root",
                    password: "123456",
                    url: "localhost:3306/"
                }
            }
        },
        methods: {
           
            connect() {
                let _this = this;
                this.db.url = "jdbc:mysql://" + this.db.url + "?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai";
                axios.post('/connect', this.db)
                    .then(function (response) {
                        _this.msg = response.data.msg;
                        _this.db = {
                            username: "root",
                            password: "123456",
                            url: "localhost:3306/"
                        }
                        _this.connectbtnenabled = false;
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            }
        }
    })
</script>
</body>
</html>

三、加载数据表信息

1.服务器端编写

columnclass 用来描述表中的每一列

package com.example.generate_code.model;

/**
 * @author: 王泽
 */

public class columnclass {
    private string propertyname; //对应java属性的名字
    private string columnname;  //数据库中的名字
    private string type;        //字段类型
    private string remark;      //备注
    private boolean isprimary;  //字段是不是一个主键

    @override
    public string tostring() {
        return "columnclass{" +
                "propertyname='" + propertyname + '\'' +
                ", columnname='" + columnname + '\'' +
                ", type='" + type + '\'' +
                ", remark='" + remark + '\'' +
                ", isprimary=" + isprimary +
                '}';
    }

    public string getpropertyname() {
        return propertyname;
    }

    public void setpropertyname(string propertyname) {
        this.propertyname = propertyname;
    }

    public string getcolumnname() {
        return columnname;
    }

    public void setcolumnname(string columnname) {
        this.columnname = columnname;
    }

    public string gettype() {
        return type;
    }

    public void settype(string type) {
        this.type = type;
    }

    public string getremark() {
        return remark;
    }

    public void setremark(string remark) {
        this.remark = remark;
    }

    public boolean getprimary() {
        return isprimary;
    }

    public void setprimary(boolean primary) {
        isprimary = primary;
    }


}

描述一个具体的表的信息 tableclass

package com.example.generate_code.model;

import java.util.list;

/**
 * @author: 王泽
 */

public class tableclass {
    private string tablename;  //表名 ,以下是生成的名字
    private string modelname;
    private string servicename;
    private string mappername;
    private string controllername;
    private string packagename;
    private list<columnclass> columns; // 字段

    @override
    public string tostring() {
        return "tableclass{" +
                "tablename='" + tablename + '\'' +
                ", modelname='" + modelname + '\'' +
                ", servicename='" + servicename + '\'' +
                ", mappername='" + mappername + '\'' +
                ", controllername='" + controllername + '\'' +
                ", packagename='" + packagename + '\'' +
                ", columns=" + columns +
                '}';
    }

    public string gettablename() {
        return tablename;
    }

    public void settablename(string tablename) {
        this.tablename = tablename;
    }

    public string getmodelname() {
        return modelname;
    }

    public void setmodelname(string modelname) {
        this.modelname = modelname;
    }

    public string getservicename() {
        return servicename;
    }

    public void setservicename(string servicename) {
        this.servicename = servicename;
    }

    public string getmappername() {
        return mappername;
    }

    public void setmappername(string mappername) {
        this.mappername = mappername;
    }

    public string getcontrollername() {
        return controllername;
    }

    public void setcontrollername(string controllername) {
        this.controllername = controllername;
    }

    public string getpackagename() {
        return packagename;
    }

    public void setpackagename(string packagename) {
        this.packagename = packagename;
    }

    public list<columnclass> getcolumns() {
        return columns;
    }

    public void setcolumns(list<columnclass> columns) {
        this.columns = columns;
    }

}

创建配置接口controller

用map来接受前端传来的数据

用到了谷歌提供的工具包guava,需要导入依赖

 @postmapping("/config")
    public respbean config(@requestbody map<string, string> map) {
        string packagename = map.get("packagename");
        try {
            connection connection = dbutils.getconnection();
            databasemetadata metadata = connection.getmetadata();
            resultset tables = metadata.gettables(connection.getcatalog(), null, null, null);
            list<tableclass> tableclasslist = new arraylist<>();
            while (tables.next()) {
                tableclass tableclass = new tableclass();
                tableclass.setpackagename(packagename);
                string table_name = tables.getstring("table_name");
                string modelname = caseformat.lower_underscore.to(caseformat.upper_camel, table_name);
                tableclass.settablename(table_name);
                tableclass.setmodelname(modelname);
                tableclass.setcontrollername(modelname + "controller");
                tableclass.setmappername(modelname + "mapper");
                tableclass.setservicename(modelname+"service");
                tableclasslist.add(tableclass);
            }
            return respbean.ok("数据库信息读取成功", tableclasslist);
        } catch (exception e) {
            e.printstacktrace();
        }
        return respbean.error("数据库信息读取失败");
    }
<dependency>
            <groupid>com.google.guava</groupid>
            <artifactid>guava</artifactid>
            <version>30.1-jre</version>
        </dependency>

2.完善index页面

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>代码生成工具</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
    <table>
        <tr>
            <td>
                <el-tag size="mini">数据库用户名:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.username"></el-input>
            </td>
        </tr>
        <tr>
            <td>
                <el-tag size="mini">数据库密码:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.password"></el-input>
            </td>
        </tr>
        <tr>
            <td>
                <el-tag size="mini">数据库连接地址:</el-tag>
            </td>
            <td>
                <el-input size="mini" v-model="db.url">
                    <template slot="prepend">jdbc:mysql://</template>
                    <template slot="append">
                        ?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai
                    </template>
                </el-input>
            </td>
        </tr>
    </table>
    <div style="display: flex">
        <el-button type="primary" size="mini" @click="connect" :disabled="!connectbtnenabled">连接数据库</el-button>
        <div style="color: #ff018d;font-weight: bold">[{{msg}}]</div>
        <el-input v-model="packagename" size="mini" style="width: 300px"></el-input>
        <el-button type="primary" size="mini" @click="config">配置</el-button>
    </div>
    <el-table
            :data="tabledata"
            stripe
            border
            style="width: 100%">
        <el-table-column
                prop="tablename"
                label="表名称"
                width="180">
        </el-table-column>
        <el-table-column
                label="实体类名称"
                width="180">
            <template slot-scope="scope">
                <el-input v-model="scope.row.modelname"></el-input>
            </template>
        </el-table-column>
        <el-table-column
                label="mapper名称">
            <template slot-scope="scope">
                <el-input v-model="scope.row.mappername"></el-input>
            </template>
        </el-table-column>
        <el-table-column
                label="service名称">
            <template slot-scope="scope">
                <el-input v-model="scope.row.servicename"></el-input>
            </template>
        </el-table-column>
        <el-table-column
                label="controller名称">
            <template slot-scope="scope">
                <el-input v-model="scope.row.controllername"></el-input>
            </template>
        </el-table-column>
    </el-table>
    <div>
        <el-button @click="generatecode" type="success">生成代码</el-button>
        <div style="color: #ff0114;font-weight: bold">*{{result}}*</div>
        <div>{{codepath}}</div>
    </div>
</div>
<script>
    new vue({
        el: "#app",
        data: function () {
            return {
                tabledata: [],
                packagename: 'com.wangze.test',
                msg: '数据库未连接',
                connectbtnenabled: true,
                db: {
                    username: "root",
                    password: "123456",
                    url: "localhost:3306/"
                }
            }
        },
        methods: {
            generatecode() {
                let _this = this;
                axios.post('/generatecode', this.tabledata)
                    .then(function (response) {
                        _this.result = response.data.msg;
                        _this.codepath = response.data.obj;
                    })
                    .catch(function (error) {
                    });
            },
            config() {
                let _this = this;
                axios.post('/config', {packagename: this.packagename})
                    .then(function (response) {
                        _this.msg = response.data.msg;
                        _this.tabledata = response.data.obj;
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            },
            connect() {
                let _this = this;
                this.db.url = "jdbc:mysql://" + this.db.url + "?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai";
                axios.post('/connect', this.db)
                    .then(function (response) {
                        _this.msg = response.data.msg;
                        _this.db = {
                            username: "root",
                            password: "123456",
                            url: "localhost:3306/"
                        }
                        _this.connectbtnenabled = false;
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            }
        }
    })
</script>
</body>
</html>

四、代码生成

1.创建模板 model.java.ftl

package ${packagename}.model;

import java.util.date;

public class ${modelname}{

    <#if columns??>
        <#list columns as column>
            <#if column.type='varchar'||column.type='text'||column.type='char'>
                /**
                * ${column.remark}
                */
                private string ${column.propertyname?uncap_first};
            </#if>
            <#if column.type='int'>
                /**
                * ${column.remark}
                */
                private integer ${column.propertyname?uncap_first};
            </#if>
            <#if column.type='datetime'>
                /**
                * ${column.remark}
                */
                private date ${column.propertyname?uncap_first};
            </#if>
            <#if column.type='bigint'>
                /**
                * ${column.remark}
                */
                private long ${column.propertyname?uncap_first};
            </#if>
            <#if column.type='double'>
                /**
                * ${column.remark}
                */
                private double ${column.propertyname?uncap_first};
            </#if>
            <#if column.type='bit'>
                /**
                * ${column.remark}
                */
                private boolean ${column.propertyname?uncap_first};
            </#if>
        </#list>
    </#if>
    <#if columns??>
        <#list columns as column>
            <#if column.type='varchar'||column.type='text'||column.type='char'>
                public string get${column.propertyname}(){
                    return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(string ${column.propertyname?uncap_first}){
                    this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
            <#if column.type='int'>
                public integer get${column.propertyname}(){
                return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(integer ${column.propertyname?uncap_first}){
                this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
            <#if column.type='datetime'>
                public date get${column.propertyname}(){
                return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(date ${column.propertyname?uncap_first}){
                this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
            <#if column.type='bigint'>
                public long get${column.propertyname}(){
                return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(long ${column.propertyname?uncap_first}){
                this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
            <#if column.type='double'>
                public double get${column.propertyname}(){
                return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(double ${column.propertyname?uncap_first}){
                this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
            <#if column.type='bit'>
                public boolean get${column.propertyname}(){
                return ${column.propertyname?uncap_first};
                }
                public void set${column.propertyname}(boolean ${column.propertyname?uncap_first}){
                this.${column.propertyname?uncap_first}=${column.propertyname?uncap_first};
                }
            </#if>
        </#list>
    </#if>
}

service.java.ftl

package ${packagename}.service;

import ${packagename}.model.${modelname};
import ${packagename}.mapper.${mappername};
import org.springframework.stereotype.service;
import org.springframework.beans.factory.annotation.autowired;
import java.util.list;

@service
public class ${servicename}{

    @autowired
    ${mappername} ${mappername?uncap_first};
    public list<${modelname}> getall${modelname}s(){
        return ${mappername?uncap_first}.getall${modelname}s();
    }
}

controller.java.ftl

package ${packagename}.controller;

import ${packagename}.model.${modelname};
import ${packagename}.service.${servicename};
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.restcontroller;
import org.springframework.web.bind.annotation.getmapping;
import java.util.list;

@restcontroller
public class ${controllername}{

    @autowired
    ${servicename} ${servicename?uncap_first};

    @getmapping("/${modelname?lower_case}s")
    public list<${modelname}> getall${modelname}s(){
        return ${servicename?uncap_first}.getall${modelname}s();
    }
}

mapper.java.ftl

package ${packagename}.mapper;

import ${packagename}.model.${modelname};
import org.apache.ibatis.annotations.mapper;
import java.util.list;

@mapper
public interface ${mappername}{
    list<${modelname}> getall${modelname}s();
}

mapper.xml.ftl

<!doctype mapper
        public "-//mybatis.org//dtd mapper 3.0//en"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${packagename}.mapper.${mappername}">

    <resultmap id="baseresultmap" type="${packagename}.model.${modelname}">
        <#list columns as column>
            <<#if column.primary??>id<#else>result</#if> column="${column.columnname}" property="${column.propertyname?uncap_first}" jdbctype="<#if column.type='int'>integer<#elseif column.type='datetime'>timestamp<#elseif column.type='text'>varchar<#else>${column.type}</#if>" />
        </#list>
    </resultmap>

    <select id="getall${modelname}s" resultmap="baseresultmap">
        select * from ${tablename};
    </select>
</mapper>

2.代码生成controller

package com.example.generate_code.controller;
import com.example.generate_code.model.respbean;
import com.example.generate_code.model.tableclass;
import com.example.generate_code.service.generatecodeservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.postmapping;
import org.springframework.web.bind.annotation.requestbody;
import org.springframework.web.bind.annotation.restcontroller;

import javax.servlet.http.httpservletrequest;
import java.util.list;

@restcontroller
public class generatecodecontroller {
    @autowired
    generatecodeservice generatecodeservice;


    @postmapping("/generatecode")
    public respbean generatecode(@requestbody list<tableclass> tableclasslist, httpservletrequest req) {
        return generatecodeservice.generatecode(tableclasslist, req.getservletcontext().getrealpath("/"));
    }
}

3.编写service

package com.example.generate_code.service;

import com.example.generate_code.model.columnclass;
import com.example.generate_code.model.respbean;
import com.example.generate_code.model.tableclass;
import com.example.generate_code.utils.dbutils;
import com.google.common.base.caseformat;
import freemarker.cache.classtemplateloader;
import freemarker.template.configuration;
import freemarker.template.template;
import freemarker.template.templateexception;

import org.springframework.stereotype.service;

import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.outputstreamwriter;
import java.sql.connection;
import java.sql.databasemetadata;
import java.sql.resultset;
import java.util.arraylist;
import java.util.list;


@service
public class generatecodeservice {

    configuration cfg = null;

    {
        cfg = new configuration(configuration.version_2_3_30);
        cfg.settemplateloader(new classtemplateloader(generatecodeservice.class, "/templates"));
        cfg.setdefaultencoding("utf-8");
    }

    public respbean generatecode(list<tableclass> tableclasslist, string realpath) {
        try {
            template modeltemplate = cfg.gettemplate("model.java.ftl");
            template mapperjavatemplate = cfg.gettemplate("mapper.java.ftl");
            template mapperxmltemplate = cfg.gettemplate("mapper.xml.ftl");
            template servicetemplate = cfg.gettemplate("service.java.ftl");
            template controllertemplate = cfg.gettemplate("controller.java.ftl");
            connection connection = dbutils.getconnection();
            databasemetadata metadata = connection.getmetadata();
            for (tableclass tableclass : tableclasslist) {
                resultset columns = metadata.getcolumns(connection.getcatalog(), null, tableclass.gettablename(), null);
                resultset primarykeys = metadata.getprimarykeys(connection.getcatalog(), null, tableclass.gettablename());
                list<columnclass> columnclasslist = new arraylist<>();
                while (columns.next()) {
                    string column_name = columns.getstring("column_name");
                    string type_name = columns.getstring("type_name");
                    string remarks = columns.getstring("remarks");
                    columnclass columnclass = new columnclass();
                    columnclass.setremark(remarks);
                    columnclass.setcolumnname(column_name);
                    columnclass.settype(type_name);
                    columnclass.setpropertyname(caseformat.lower_underscore.to(caseformat.upper_camel, column_name));
                    primarykeys.first();
                    while (primarykeys.next()) {
                        string pkname = primarykeys.getstring("column_name");
                        if (column_name.equals(pkname)) {
                            columnclass.setprimary(true);
                        }
                    }
                    columnclasslist.add(columnclass);
                }
                tableclass.setcolumns(columnclasslist);
                string path = realpath + "/" + tableclass.getpackagename().replace(".", "/");
                generate(modeltemplate, tableclass, path + "/model/");
                generate(mapperjavatemplate, tableclass, path + "/mapper/");
                generate(mapperxmltemplate, tableclass, path + "/mapper/");
                generate(servicetemplate, tableclass, path + "/service/");
                generate(controllertemplate, tableclass, path + "/controller/");
            }
            return respbean.ok("代码已生成", realpath);
        } catch (exception e) {
            e.printstacktrace();
        }
        return respbean.error("代码生成失败");
    }

    private void generate(template template, tableclass tableclass, string path) throws ioexception, templateexception {
        file folder = new file(path);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        string filename = path + "/" + tableclass.getmodelname() + template.getname().replace(".ftl", "").replace("model", "");
        fileoutputstream fos = new fileoutputstream(filename);
        outputstreamwriter out = new outputstreamwriter(fos);
        template.process(tableclass,out);
        fos.close();
        out.close();
    }
}

五、测试

这时候已经找到了,我们来验证一下效果!

修改写配置

spring.datasource.name=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/boot_crm?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai

pom.xml

<resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>

导入生成的代码

运行测试

一个基本的mybatis逆向工程就完成了!

最后附上项目源代码:gitee

到此这篇关于springboot整合freemarker代码自动生成器的文章就介绍到这了,更多相关springboot 代码自动生成器内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!