一,前提知識儲備
1,什么是FreeMarker?
FreeMarker
是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML
網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java
類庫,是一款程序員可以嵌入他們所開發產品的組件。
2,FreeMarker的基本用法
下面使用測試類來說明FreeMarker
的基本用法,其實FreeMarker
就是一個模板引擎,和thymeleaf
和beetle
等都是一個品種的,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
模板即可!