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

springboot整合freemarker代码自动生成器

程序员文章站 2022-06-27 18:39:07
手撸一个代码自动生成器!!实现功能:mybatis 逆向工程技术架构页面是用 vue ,element-ui开发;网络请求是 axios。服务端是 spring boot页面模版是 freemarke...

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

实现功能:mybatis 逆向工程

技术架构

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

springboot整合freemarker代码自动生成器

开发步骤:

一、创建工程

springboot整合freemarker代码自动生成器

二、数据库连接操作

1.所需包结构

springboot整合freemarker代码自动生成器

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>

springboot整合freemarker代码自动生成器

三、加载数据表信息

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>

springboot整合freemarker代码自动生成器

四、代码生成

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();
    }
}

五、测试

springboot整合freemarker代码自动生成器

springboot整合freemarker代码自动生成器

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

springboot整合freemarker代码自动生成器

修改写配置

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>

导入生成的代码

springboot整合freemarker代码自动生成器

运行测试

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

最后附上项目源代码:gitee

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