springboot集成FreeMarker實現自動生成增刪改查代碼

一,前提知識儲備

1,什麼是FreeMarker?

FreeMarker 是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。

2,FreeMarker的基本用法

下面使用測試類來說明FreeMarker的基本用法,其實FreeMarker就是一個模板引擎,和thymeleafbeetle等都是一個品種的,FreeMarker最初的目的只是爲了渲染html頁面的,不過它可以渲染任何文件,只要你遵循它指定好的語法即可!

(1)FreeMarker依賴

首先創建好一個基本的springboot工程,然後導入FreeMarker依賴,然後就可以開始測試了。

		<dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>

(2)指定模板並生成

說明:文件路徑加載器加載的是存放模板的文件夾的絕對路徑,數據模型是Map<String,Object>,給裏面存放數據即可,process方法的參數1是數據模型,參數2是Writer對象,執行此方法FreeMarker會自動渲染頁面。

 @Test
   public void test0() throws Exception {
        //1,創建FreeMarker的配置類
        Configuration cfg=new Configuration();
        //2,指定模板加載器,將模板加入緩存中
        //文件路徑加載器,獲取到templates文件的路徑
        String templates = this.getClass().getClassLoader().getResource("templates").getPath();
        FileTemplateLoader fileTemplateLoader=new FileTemplateLoader(new File(templates));
        cfg.setTemplateLoader(fileTemplateLoader);
        //3,獲取模板
        Template template = cfg.getTemplate("test.ftl");
        //4,構造數據模型
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("username","測試人員");
        map.put("password",1234);

        List<String> list=new ArrayList<>();
        list.add("第一個");
        list.add("第二個");
        map.put("list",list);

        //5,文件輸出
        /**
         * 處理模型
         *      參數一 數據模型
         *      參數二 writer對象(FileWriter(文件輸出),printWriter(控制檯輸出))
         */
//        template.process(map,new FileWriter(new File("D:\\a.txt")));
        template.process(map,new PrintWriter(System.out));
    }

test.ftl模板

<#-- assign指令 在ftl模板中定義數據存入到root節點下 -->
<#assign name="傻子">
<#--然後就可以取出name的值-->
${name}

你好,${username}

<#--- if指令 -->
<#if password=1234>
簡單密碼
<#elseif password=123456>
一般密碼
<#else>
複雜密碼
</#if>

<#-- list指令 迭代循環 -->
<#list list as abc>
    ${abc}
</#list>

<#--模板包含,這樣生成模板test.ftl的時候同時會包含模板test2.ftl-->
<#include "test2.ftl">

(3)指定字符串模板並生成

 //字符串模板
    public static void main(String[] args) throws Exception {
        //1,創建配置對象
        Configuration cfg=new Configuration();
        //2,指定加載器
        cfg.setTemplateLoader(new StringTemplateLoader());
        //3,創建字符串模板
        //字符串
        String templateString="歡迎您,${username}";
        //通過字符串創建模板
        Template template=new Template("templateNames",new StringReader(templateString),cfg);
        //4,構造數據
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("username","測試人員");
        map.put("password",1234);

        List<String> list=new ArrayList<>();
        list.add("第一個");
        list.add("第二個");
        map.put("list",list);

        //5,處理模板
        template.process(map,new PrintWriter(System.out));
    }

3,測試數據庫元數據的操作

(1)獲取數據庫基本信息

	 @Test
    public void test01() throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";//沒有指定具體哪個數據庫,現在獲取的是整個連接
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        //獲取數據庫基本信息
        System.out.println(metaData.getUserName());
        System.out.println(metaData.supportsTransactions());//是否支持事務
        System.out.println(metaData.getDatabaseProductName());//數據庫類型(MYSQL)
        connection.close();

        /**
         * 打印結果如下
         * root@localhost
         * true
         * MySQL
         */
    }

(2)獲取數據庫列表

	@Test
    public void test2() throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";//沒有指定具體哪個數據庫,現在獲取的是整個連接
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        //獲取數據庫列表名稱
        ResultSet resultSet = metaData.getCatalogs();
        while (resultSet.next()){
            System.out.println(resultSet.getString(1));
        }
        resultSet.close();
        connection.close();
        /**
         * 打印連接中的所有數據庫名稱
         * activiti
         * dage
         * dk
         * dk1
         * dk_front
         * dk_front1
         * information_schema
         * laji
         * light_master
         * mysql
         * performance_schema
         * sakila
         * solr
         * sys
         * test
         * workflow
         * world
         */
    }

(3)獲取指定數據庫中的表信息

打印出該數據庫下的所有表名。

 @Test
    public void test3() throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://127.0.0.1:3306/laji?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true";//指定了數據庫
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        //獲取數據庫中表信息(mysql可以這樣寫,oracle會有一點區別)
        //參數1:當前操作的數據庫 參數2:mysql可爲空,oracle填寫用戶名(要大寫) 參數3:null是查詢所有表 非空是查詢目標表 參數4:類型 table是表,view是視圖
        ResultSet resultSet = metaData.getTables("laji", null, null, new String[]{"TABLE"});
        while (resultSet.next()){
        	//會打印出該數據庫下的所有表名
            System.out.println(resultSet.getString("TABLE_NAME"));
        }
        resultSet.close();
        connection.close();
    }

(4)獲取指定表中的字段信息

打印出指定表的所有字段名。

 public static void main(String[] args) throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://127.0.0.1:3306/laji?characterEncoding=utf8&serverTimezone=UTC";//指定了數據庫
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        ResultSet city = metaData.getColumns("laji", null, "user", null);
        while (city.next()){
        	//會打印出指定表的所有字段名
            System.out.println(city.getString("COLUMN_NAME"));
        }
    }

(5)測試參數元數據

/**
     * 測試參數元數據
     * 通過preparedStatement獲取
     * 目的:獲取sql參數中的屬性信息
     */
    @Test
    public void test4() throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://127.0.0.1:3306/laji?characterEncoding=utf8&serverTimezone=UTC";//指定了數據庫
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        String sql="select * from user where id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,1);//設第一個參數爲int 1
        //獲取參數元數據
        ParameterMetaData metaData = preparedStatement.getParameterMetaData();
        //得到參數的個數
        int count = metaData.getParameterCount();//打印 1 (只有一個id參數)

        System.out.println(count);
        preparedStatement.close();
        connection.close();
    }

(6)測試結果集元數據

 @Test
    public void test5() throws Exception{
        String driver="com.mysql.cj.jdbc.Driver";
        String url="jdbc:mysql://127.0.0.1:3306/laji?characterEncoding=utf8&serverTimezone=UTC";//指定了數據庫
        String username="root";
        String password="1234";

        //獲取連接
        Class.forName(driver);//註冊驅動
        Connection connection = DriverManager.getConnection(url, username,password);

        String sql="select * from user where id=?";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1,1);//設第一個參數爲int 1
        //查詢
        ResultSet resultSet = preparedStatement.executeQuery();

        //獲取結果集元數據
        ResultSetMetaData metaData = resultSet.getMetaData();
        //獲取查詢字段個數
        int count = metaData.getColumnCount();
        for(int i=1;i<=count;i++){
            //獲取列名
            String columnName = metaData.getColumnName(i);//第i個列
            //獲取字段類型  sql類型 varchar
            int columnType = metaData.getColumnType(i);
            //獲取java類型  String
            String columnClassName = metaData.getColumnClassName(i);
            System.out.println(columnName+"---"+columnType+"---"+columnClassName);
        }

        preparedStatement.close();
        connection.close();
    }
    /**
     * 打印
     * id---4---java.lang.Integer
     * account---12---java.lang.String
     * password---12---java.lang.String
     * islogin---4---java.lang.Integer
     */

二,生成代碼實戰

1,首先準備四個實體類

DataBase實體

package com.ftx.demo.model;

/**
 * @author FanJiangFeng
 * @version 1.0.0
 * @ClassName DataBase.java
 * @Description TODO
 * @createTime 2020年04月23日 14:15:00
 */
public class DataBase {

    private static String mysqlUrl="jdbc:mysql://[ip]:[port]/[db]?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";
    private static String oracleUrl="jdbc:oracle:thin:@[ip]:[port]:[db]";

    private String dbType;//數據庫類型
    private String userName;
    private String passWord;
    private String driver;
    private String url;

    public DataBase(){}
    public DataBase(String dbType){
        this(dbType,"127.0.0.1","3306","");
    }
    public DataBase(String dbType,String db){
        this(dbType,"127.0.0.1","3306",db);
    }
    /**
     *
     * @param dbType    數據庫類型 mysql/oracle
     * @param ip        ip
     * @param port      3306
     * @param db        數據庫名稱 test
     */
    public DataBase(String dbType,String ip,String port,String db){
        this.dbType=dbType;
        if("MYSQL".equals(dbType.toUpperCase())){
            this.driver="com.mysql.cj.jdbc.Driver";
            this.url=mysqlUrl.replace("[ip]",ip).replace("[port]",port).replace("[db]",db);
        }else{
            this.driver="oracle.jdbc.driver.OracleDriver";
            this.url=oracleUrl.replace("[ip]",ip).replace("[port]",port).replace("[db]",db);
        }
    }
}

Settings實體

public class Settings {
    private String project="example";
    private String pPackage="com.example.demo";
    private String projectComment;
    private String author;
    private String path1="com";
    private String path2="example";
    private String path3="demo";
    private String pathAll;
}

Table實體

//表實體
public class Table {
    private String name;//表名稱
    private String name2;//處理後的表名稱
    private String comment;//介紹
    private String key;//主鍵列
    private List<Column> columnList;

Column實體

public class Column {
    private String columnName;//列名稱
    private String columnName2;//處理後的列名稱
    private String columnType;//列類型
    private String columnDbType;//列在數據庫中的類型

    //本工程暫不處理備註和主鍵
    private String columnComment;//列備註id
    private String columnKey;//是否是主鍵

2,自定義配置文件

自定義配置文件中配置數據庫和Java對應的數據類型
在這裏插入圖片描述

3,準備三個工具類

FileUtils工具類
該工具類用到的主要是 查詢整個目錄的文件夾 和 遞歸獲取某個目錄下的所有文件夾 的方法

package com.ftx.demo.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

//文件處理工具類
public class FileUtils {
    //得到相對路徑
    public static String getRelativePath(File baseDir,File file){
        if(baseDir.equals(file)){
            return "";
        }
        if(baseDir.getParentFile()==null){
            return file.getAbsolutePath().substring(baseDir.getAbsolutePath().length());
        }else{
            return file.getAbsolutePath().substring(baseDir.getAbsolutePath().length()+1);
        }
    }
    //查詢整個目錄下的所有文件
    public static List<File> searchAllFile(File dir) throws IOException {
        ArrayList arrayList=new ArrayList();
        searchFiles(dir,arrayList);
        return arrayList;

    }
    //遞歸獲取某個目錄下的所有文件
    public static void searchFiles(File dir,List<File> collector){
        if(dir.isDirectory()){
            File[] files = dir.listFiles();
            for(int i=0;i<files.length;i++){
                searchFiles(files[i],collector);
            }
        }else{
            collector.add(dir);
        }
    }
    //創建文件
    public static File mkdir(String dir,String file){
        if(dir==null){
            throw new  IllegalArgumentException("文件夾不許爲空");
        }
        File result=new File(dir,file);
        if(result.getParentFile()!=null){
            result.getParentFile().mkdirs();
        }
        return result;
    }
}

PropertiesUtils工具類
此工具類說明:靜態代碼塊預加載,將自定義的配置文件properties的內容全部加載到customMap中,然後在其他類中調用此類獲取customMap中的鍵值對(鍵值對就是字都應以配置文件中所配置的內容)

package com.ftx.demo.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

//需要將自定義的配置信息寫入到properties文件中,配置到相對於工程的properties文件下
public class PropertiesUtils {
    public static Map<String,String> customMap=new HashMap<>();

    //靜態塊,預加載,將自定義的配置文件properties的內容全部加載到customMap中
    static {
        File dir=new File("properties");
        try {
            List<File> files = FileUtils.searchAllFile(new File(dir.getAbsolutePath()));
            for(File file:files){
                if(file.getName().endsWith("properties")){
                    Properties prop=new Properties();
                    prop.load(new FileInputStream(file));
                    customMap.putAll((Map)prop);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //測試預加載是否成功(看是否打印出了properties配置文件的key和value)
    public static void main(String[] args) {
       for(String key:customMap.keySet()){
           System.out.println(key+"---"+customMap.get(key));
       }
    }
}

DataBaseUtils工具類

方法介紹:
1,獲取數據庫連接
2,獲取數據庫列表
3,獲取數據庫中的所有表和字段並構造實體類
4,根據表名的截取操作生成類名

package com.ftx.demo.util;
import com.ftx.demo.model.Column;
import com.ftx.demo.model.DataBase;
import com.ftx.demo.model.Table;
import org.junit.Test;
import javax.servlet.http.HttpSession;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class DataBaseUtils {
    //獲取數據庫連接
    public static Connection getConnection(DataBase db) throws Exception{
        //獲取連接
        Class.forName(db.getDriver());//註冊驅動
        Connection connection = DriverManager.getConnection(db.getUrl(), db.getUserName(),db.getPassWord());
        return connection;
    }

    //獲取數據庫列表
    public static List<String> getShemas(DataBase db) throws Exception{
        Connection connection = getConnection(db);
        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        //獲取所有數據庫列表
        ResultSet resultSet = metaData.getCatalogs();
        List<String> list=new ArrayList<>();
        while(resultSet.next()){
            list.add(resultSet.getString(1));
        }
        resultSet.close();
        connection.close();
        return list;
    }

    //獲取數據庫中的所有表和字段並構造實體類(相當於一鍵生成數據庫中所有表的增刪改查代碼)
    public static List<Table> getDbInfo(DataBase db, HttpSession session) throws Exception{
        //獲取連接
        Connection connection = getConnection(db);
        //獲取元數據
        DatabaseMetaData metaData = connection.getMetaData();
        List<Table> list=new ArrayList<>();
        //獲取當前數據庫的所有表
        String dbss=(String) session.getAttribute("db");
        ResultSet tables = metaData.getTables(dbss, null, null, new String[]{"TABLE"});
        while (tables.next()){
            //表名
            String table_name = tables.getString("TABLE_NAME");
            //構造生成對應實體類的類名
            String className = removePrefix(table_name);
            //主鍵
            ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, table_name);
            //對主鍵遍歷的原因(或許一張表有多個主鍵)
            String keys="";
            while (primaryKeys.next()){
                String keyName = primaryKeys.getString("COLUMN_NAME");
                keys+=keyName+",";
            }
            Table tab=new Table();
            tab.setName(table_name);
            tab.setName2(className);
            tab.setKey(keys);
            //處理表中的所有字段
            ResultSet columns = metaData.getColumns(dbss, null, table_name, null);
            List<Column> cols=new ArrayList<>();
            while (columns.next()){
                Column column=new Column();
                //列名稱
                String column_name = columns.getString("COLUMN_NAME");
                //java實體的屬性名
                String attName = column_name;
                //java類型和數據庫類型
                String type_name = columns.getString("TYPE_NAME");
                String javaType = PropertiesUtils.customMap.get(type_name);
                column.setColumnName(column_name);
                column.setColumnName2(attName);
                column.setColumnDbType(type_name);
                column.setColumnType(javaType);
                cols.add(column);
            }
            tab.setColumnList(cols);
            list.add(tab);
            //關閉連接,釋放資源
            columns.close();
            primaryKeys.close();
        }
        tables.close();
        connection.close();
        return list;

    }

    //根據表名的截取操作生成類名
    public static String removePrefix(String tableName){
        //從自定義的配置文件中拿到前綴的配置
        String prefixes = PropertiesUtils.customMap.get("tableRemovePrefixes");
        //這裏就不字符串處理了,直接把表名當類名用了
        String replace = tableName;
        return replace;
    }

    // 測試 獲取數據庫中的所有表和字段並構造實體類 的方法是否可用
//    public static void main(String[] args) throws Exception {
//        DataBase db=new DataBase("MYSQL","laji");
//        db.setUserName("root");
//        db.setPassWord("1234");
//        List<Table> dbInfo = DataBaseUtils.getDbInfo(db);
//        for(Table table:dbInfo){
//            System.out.println(dbInfo);
//        }
//    }
}

4,準備生成代碼的模板

resources靜態資源中添加生成代碼的FreeMarker模板,如下圖
在這裏插入圖片描述

package ${pPackage}.dao;

import java.util.List;
import java.util.Map;

public interface ${table.name?cap_first}Dao {

    /**
     * 查詢
     */
    List<Map> get${table.name?cap_first}List();

    /**
     * 添加
     */
    int add${table.name?cap_first}(Map map);

    /**
     * 待修改
     */
    Map toEdit${table.name?cap_first}(${table.columnList[0].columnType} ${table.columnList[0].columnName});

    /**
     * 修改
     */
    int update${table.name?cap_first}(Map map);

    /**
     * 刪除
     */
    int delete${table.name?cap_first}(${table.columnList[0].columnType} ${table.columnList[0].columnName});

}

package ${pPackage}.model;


public class ${table.name?cap_first} {  //?cap_first freemarker內置函數 首字母大寫

<#list table.columnList as abc>
private ${abc.columnType} ${abc.columnName};
</#list>



<#list table.columnList as abc>
public ${abc.columnType} get${abc.columnName?cap_first}() {
        return this.${abc.columnName};
        }

public void set${abc.columnName?cap_first}(String ${abc.columnName}) {
        this.${abc.columnName} = ${abc.columnName};
        }
</#list>


        }

5,html頁面(web頁面進行生成)

index.html首頁


    <table style="height: 100%;width: 100%;" border="3">
        <form action="">
        <tr>
            <td>數據庫類型</td>
            <td><select name="dbKind" id="dbKind">
                <option value="MYSQL">MYSQL</option>
                <option value="ORACLE">ORACLE</option>
            </select></td>
        </tr>
        <tr>
            <td>數據庫IP</td>
            <td><input id="ip" type="text" name="ip" required><span style="color: red;">*必填項</span></td>
        </tr>
        <tr>
            <td>端口號</td>
            <td><input id="port" type="text" name="port" required><span style="color: red;">*必填項</span></td>
        </tr>
        <tr>
            <td>用戶名</td>
            <td><input id="username" type="text" name="username" required><span style="color: red;">*必填項</span></td>
        </tr>
        <tr>
            <td>密碼</td>
            <td><input id="password" type="password" name="password" required><span style="color: red;">*必填項</span></td>
        </tr>
        <tr>
            <td><input id="test" type="button" value="測試連接"><span style="color: red;">*必選項</span></td>
            <td>選擇數據庫  <select name="databases" id="databases">

            </select></td>
            <td><input id="next" type="button" value="下一步"></td>
        </tr>
        </form>
    </table>

<script th:src="@{../jquery.min.js}"></script>
<script>
    $("#test").click(function () {
          $.post(
              "/testConnection",
              {dbKind:$("#dbKind").val(),ip:$("#ip").val(),port:$("#port").val(),username:$("#username").val(),password:$("#password").val()},
              function (data) {
                 if(data.length>0){
                     alert("連接成功");
                     var temp="";
                     for(var i=0;i<data.length;i++){
                         temp+='<option value="'+data[i]+'">'+data[i]+'</option>';
                     }
                     $("#databases").html(temp);
                 }else{
                     alert("連接失敗");
                 }
              },
              "json"
          );
    });
    $("#next").click(function () {
        location.href="/enter1?databases="+$("#databases").val()+"&username="+$("#username").val()+"&password="+$("#password").val();
    });
</script>

index1.html下一步的頁面



    <table style="height: 100%;width: 100%;" border="3">
        <form action="/create" method="post">
        <tr>
            <td>模板</td>
            <td><select name="dbKind" id="dbKind">
                <option value="springboot程序的模板">springboot程序的模板</option>
            </select></td>
        </tr>
        <tr>
            <td>代碼生成路徑</td>
            <td><input id="fileUrl" type="text" name="fileUrl" /><span style="color: red;">*必填項</span></td>
        </tr>
        <tr>
            <td>項目名</td>
            <td><input id="projectEngName" type="text" name="projectEngName" required></td>
        </tr>
        <tr>
            <td>包名</td>
            <td><input id="packagename" type="text" name="packagename" required><span style="color: red;">*必填項(格式必須爲com.ftx.demo這種)</span></td>
        </tr>
        <tr>
            <td>項目中文名稱</td>
            <td><input id="projectChinaName" type="text" name="projectChinaName" required></td>
        </tr>
            <tr>
                <td>數據庫</td>
                <td><input id="db" type="text" name="db" th:value="*{databases}" readonly></td>
            </tr>
            <tr>
                <td>用戶名</td>
                <td><input id="root" type="text" name="root" th:value="*{username}" readonly></td>
            </tr>
            <tr>
                <td>密碼</td>
                <td><input id="psd" type="text" name="psd" th:value="*{password}" readonly></td>
            </tr>
        <tr>
            <td><input id="test" type="submit" value="生成代碼"></td>
            <td><input type="button" value="關閉"></td>
        </tr>
        </form>
    </table>

result.html反饋結果頁面

<h3 style="text-align:center;color: blue;" th:text="${success}"></h3>

6,controller類

package com.ftx.demo.controller;
import com.ftx.demo.core.GeneratorFacade;
import com.ftx.demo.model.DataBase;
import com.ftx.demo.model.Settings;
import com.ftx.demo.util.DataBaseUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;

@Controller
public class GeneratorController {
    @RequestMapping("/enter")
    public String index(){
        return "index.html";
    }
    @RequestMapping("/enter1")
    public String index1(String databases, String username, String password, Model model){
        model.addAttribute("databases",databases);
        model.addAttribute("username",username);
        model.addAttribute("password",password);
        return "index1.html";
    }
    //點擊測試連接按鈕,填充選擇數據庫下拉框
    @RequestMapping("/testConnection")
    @ResponseBody
    public List<String> testConnection(String dbKind,String ip,String port,String username,String password) throws Exception{
        DataBase dataBase=new DataBase(dbKind,ip,port,"");
        dataBase.setUserName(username);
        dataBase.setPassWord(password);
        List<String> dbs = DataBaseUtils.getShemas(dataBase);
        if(dbs.size()>0){
           return dbs;
        }else{
            return null;
        }
    }

    //生成代碼
    /**
     * 確認生成按鈕傳來的參數
     * @param dbKind   選擇模板,這裏沒啥用,因爲已經寫死模板位置了  resources/springboot程序的模板  文件夾
     * @param fileUrl   代碼生成路徑
     * @param projectEngName    項目英文名稱 沒啥用
     * @param packagename      包名 com.ftx.demo
     * @param projectChinaName  項目中文名稱,也沒啥用
     * @param db    選擇的數據庫名
     * @param root  數據庫的用戶名root
     * @param psd   數據庫的密碼
     * @throws Exception
     *
     * 下面用sesson的目的很簡單,不要想多了,是存了數據庫名,到後來的方法要取出來,我這是把它當成全局變量使用了
     */
    @RequestMapping("/create")
    public String create(String dbKind, String fileUrl, String projectEngName, String packagename, String projectChinaName,
                       String db, String root, String psd, HttpSession session,Model model) throws Exception{

        Settings settings=new Settings();
        //包名
        settings.setpPackage(packagename);//com.ftx.demo
        String[] split = packagename.split("\\.");//split(".")無法分割字符串,必須加上\\
        settings.setPath1(split[0]);//com
        settings.setPath2(split[1]);//ftx
        settings.setPath3(split[2]);//demo

        //項目名(沒啥用)
        settings.setProject(projectEngName);
        //默認只支持mysql數據庫吧,oracle暫時先不處理,先寫死爲mysql
        session.setAttribute("db",db);
        DataBase dbs=new DataBase("MYSQL",db);
        dbs.setUserName(root);
        dbs.setPassWord(psd);
        GeneratorFacade generatorFacade=new GeneratorFacade(dbKind,fileUrl,settings,dbs);
        boolean b = generatorFacade.generatorByDataBase(session);
        if(b){
            model.addAttribute("success","代碼已生成,迴文件查看!");
            return "result";
        }else{
            model.addAttribute("success","代碼生成失敗!");
            return "result";
        }
    }
}

7,核心類Generator和GeneratorFacade

Generator
代碼生成器的核心處理類,使用FreeMarker完成文件生成,數據模型+模板

package com.ftx.demo.core;
import com.ftx.demo.util.FileUtils;
import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.File;
import java.io.FileWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

//代碼生成器的核心處理類,使用FreeMarker完成文件生成,數據模型+模板
//數據:數據模型 模板的位置  生成文件的路徑
public class Generator {
    private String templatePath;//模板路徑
    private String outPath;//代碼生成路徑
    private Configuration cfg;

    public Generator(String templatePath, String outPath)throws Exception {
        this.outPath = outPath;
        //實例化Configuration對象
        cfg=new Configuration();
        //指定模板加載器
        //在代碼中動態加載jar、資源文件的時候,首先應該是使用Thread.currentThread().getContextClassLoader()。
        // 如果你使用Test.class.getClassLoader(),可能會導致和當前線程所運行的類加載器不一致(因爲Java天生的多線程)
        String templates = Thread.currentThread().getContextClassLoader().getResource("").getPath()+"\\springboot程序的模板\\";
        this.templatePath = templates;
        FileTemplateLoader fileTemplateLoader=new FileTemplateLoader(new File(templates));
        cfg.setTemplateLoader(fileTemplateLoader);
    }
    /**
     * 代碼生成
     *      1,掃描模板路徑下的所有模板
     *      2,對每個模板進行文件生成(數據模板)
     *      3,參數:數據模板
     */
    public  void scanAndGenerator(Map<String,Object> dataModel)throws Exception{
        //根據模板路徑找到此路徑下的所有模板文件
        List<File> fileList = FileUtils.searchAllFile(new File(templatePath));
        //對每個模板進行文件生成
        for(File file:fileList){
            //參數1:數據模型  參數2:文件模板
            excuteGenerator(dataModel,file);
        }
    }
    //對模板進行文件生成
    //參數1:數據模型  參數2:文件模板
    private void excuteGenerator(Map<String, Object> dataModel, File file) throws Exception {
        //1,文件路徑處理
        /**
         * file:D:/模板存在的文件夾/${path1}/${path2}/${path3}/${classname}.java  絕對路徑
         * replace的目的:得到文件名,文件名之前的路徑都不要了,只留包名之後的內容
         */
        //  得到模板文件的這樣的路徑 ${path1}/${path2}/${path3}/${className}.java
        String one=file.getAbsolutePath();
        int i = one.indexOf("$");
        String templateFileName = one.substring(i);
        //把${path1}/${path2}/${path3}/${className}.java 替換成 com/ftx/demoUser.java (數據模型中的內容)
        String outFileName = processString(templateFileName, dataModel);
        //2,讀取文件模板
        //上面把模板整個文件夾都加載到了模板加載器,所以這裏拿模板只需要傳入該文件夾下的文件名即可
        Template template = cfg.getTemplate(templateFileName);//相對路徑 ${path1}/${path2}/${path3}/${classname}.java
        template.setOutputEncoding("utf-8");//指定生成文件的字符集編碼
        //3,創建文件
        File file1 = FileUtils.mkdir(this.outPath, outFileName);
        //4,模板處理(文件生成)
        FileWriter fileWriter=new FileWriter(file1);
        System.err.println(dataModel);
        template.process(dataModel,fileWriter);
        fileWriter.close();
    }
    /**
     * 把${path1}/${path2}/${path3}/${className}.java 替換成 com/ftx/demoUser.java (數據模型中的內容)
     * FreeMarker的字符串模板 替換
     */
    public String processString(String templateString,Map dataModel) throws Exception{
        StringWriter stringWriter=new StringWriter();
        Template template=new Template("ts",new StringReader(templateString),cfg);
        template.process(dataModel,stringWriter);
        return stringWriter.toString();
    }
    /**
     * 測試代碼生成主類scanAndGenerator是否管用
     */
    public static void main(String[] args) throws Exception {
        String templatePath="D:\\工作\\學習資料\\FreeMarker\\模板";
        String outPath="D:\\工作\\學習資料\\FreeMarker\\生成路徑";
        Generator generator=new Generator(templatePath,outPath);
        Map<String,Object> dataModel=new HashMap<>();
        dataModel.put("username","張三");
        generator.scanAndGenerator(dataModel);
    }
}

GeneratorFacade
準備數據模型,調用核心處理類Generator類完成代碼生成工作

package com.ftx.demo.core;
import com.ftx.demo.model.DataBase;
import com.ftx.demo.model.Settings;
import com.ftx.demo.model.Table;
import com.ftx.demo.util.DataBaseUtils;
import com.ftx.demo.util.PropertiesUtils;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GeneratorFacade {
    //模板位置
    private String templatePath;
    //代碼生成路徑
    private String outPath;
    //工程配置對象
    private Settings settings;
    //數據庫對象
    private DataBase db;
    private Generator generator;

    public GeneratorFacade(String templatePath, String outPath, Settings settings, DataBase db) throws Exception {
        this.templatePath = templatePath;
        this.outPath = outPath;
        this.settings = settings;
        this.db = db;
        generator=new Generator(templatePath,outPath);
    }

    /**
     * 準備數據模型
     * 調用核心處理類完成代碼生成工作
     */
    public boolean generatorByDataBase(HttpSession session) throws Exception{
        List<Table> tables = DataBaseUtils.getDbInfo(db,session);
        for(Table table:tables){
            //對每個table進行代碼生成
            Map<String, Object> dataModel = getDataModel(table);
            /**
             * 得到的數據模型如下
             * {NUMBER=Long, CHAR=String, project=test, BIGINT=Long, TEXT=String, className=r, VARCHAR2=String,
             * INT=Integer, NVARCHAR2=String, DATE=java.util.Date, DATETIME=java.util.Date, path1=com, path2=ftx,
             * path3=demo, pPackage=com.ftx.demo, VARCHAR=String, testKey=testValue, DOUBLE=Double, tableRemovePrefixes="tb_,co_",
             * table=Table{name='user', name2='r', comment='null', key='userid,userno,id,Host,User,id,',
             * columnList=[Column{columnName='id', columnName2='id', columnType='Integer', columnDbType='INT', columnComment='null',
             * columnKey='null'}, Column{columnName='account', columnName2='account', columnType='String', columnDbType='VARCHAR',
             * columnComment='null', columnKey='null'}, Column{columnName='password', columnName2='password', columnType='String',
             * columnDbType='VARCHAR', columnComment='null', columnKey='null'}, Column{columnName='islogin', columnName2='islogin',
             * columnType='Integer', columnDbType='INT', columnComment='null', columnKey='null'}]}}
             */

            //調用代碼生成方法,把數據模型傳過去,進行生成
            generator.scanAndGenerator(dataModel);
        }
        return true;
    }
    /**
     * 根據table對象獲取數據模型
     * @param table
     * @return
     */
    private Map<String,Object> getDataModel(Table table) {
        Map<String,Object> map=new HashMap<>();
        //自定義配置
        map.putAll(PropertiesUtils.customMap);
        //元數據
        map.put("table",table);
        //settings
        map.put("project",this.settings.getProject());
        map.put("pPackage",this.settings.getpPackage());
        map.put("path1",this.settings.getPath1());
        map.put("path2",this.settings.getPath2());
        map.put("path3",this.settings.getPath3());
        //類名
        map.put("className",table.getName2());
        return map;
    }
}

8,運行項目

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
工程說明

demo工程啓動之後,測試連接成功之後選擇生成的數據庫,然後會生成該數據庫中所有表的增刪改查,暫時本工程只支持自動生成model實體類和dao接口,要其他的話你只需要增加FreeMarker模板即可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章