[软件开发] Spring boot 整合 FreeMarker 实现代码生成功能
作者:CC下载站 日期:2019-10-17 00:00:00 浏览:49 分类:编程开发
在我们开发一个新的功能的时候,会根据表创建Entity,Controller,Service,Repository等代码,其中很多步骤都是重复的,并且特别繁琐。这个时候就需要一个代码生成器帮助我们解决这个问题从而提高工作效率,让我们更致力于业务逻辑。
设计原理
在我们安装数据库后会有几个默认的数据库,其中information_schema这个数据库中保存了MySQL服务器所有数据库的信息,如:数据库名、数据库表、表的数据信息与访问权限等。
information_schema的表tables记录了所有数据库的表的信息
information_schema的表columns记录了所有数据库的表字段详细的信息
我们代码中可以可以通过Sql语句查询出当前数据库中所有表的信息,这里已 eladmin 为例。
# 显示部分数据:表名称、数据库引擎、编码、表备注、创建时间
select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables
where table_schema = (select database());
知道表的数据后,可以查询出表字段的详细数据,这里用 job 表为例
sql语句如下:
# 显示部分数据:字段名称、字段类型、字段注释、字段键类型等
select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns
where table_schema = (select database()) and table_name = "job";
有了表字段信息的数据后,通过程序将数据库表字段类型转换成Java语言的字段类型,再通过FreeMarker创建模板,将数据写入到模板,输出成文件即可实现代码生成功能。
代码实现
这里只贴出核心代码,源码可查询文末地址,首先创建一个新的spring boot 项目,选择如下依赖
Maven完整依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- 配置管理工具 -->
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
教程开始
修改Spring boot 配置文件 application.yml,如下
service:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/eladmin?serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
show-sql: true
在 resources 目录下创建 Mysql 字段与 Java字段对应关系的配置文件 generator.properties
,生成代码时字段转换时使用
tinyint=Integer
smallint=Integer
mediumint=Integer
int=Integer
integer=Integer
bigint=Long
float=Float
double=Double
decimal=BigDecimal
bit=Boolean
char=String
varchar=String
tinytext=String
text=String
mediumtext=String
longtext=String
date=Timestamp
datetime=Timestamp
timestamp=Timestamp
在 vo 包下创建临时 Vo 类 ColumnInfo,该类的功能用于接收Mysql字段详细信息
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ColumnInfo {
/** 数据库字段名称 **/
private Object columnName;
/** 允许空值 **/
private Object isNullable;
/** 数据库字段类型 **/
private Object columnType;
/** 数据库字段注释 **/
private Object columnComment;
/** 数据库字段键类型 **/
private Object columnKey;
/** 额外的参数 **/
private Object extra;
}
在 util 包下创建字段工具类 ColumnUtil,该类的功能用于转换mysql类型为Java字段类型,同时添加驼峰转换方法,将表名转换成类名
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
/**
* sql字段转java
*
* @author jie
* @date 2019-01-03
*/
public class ColumnUtil {
private static final char SEPARATOR = '_';
/**
* 获取配置信息
*/
public static PropertiesConfiguration getConfig() {
try {
return new PropertiesConfiguration("generator.properties");
} catch (ConfigurationException e) {
e.printStackTrace();
}
return null;
}
/**
* 转换mysql数据类型为java数据类型
* @param type
* @return
*/
public static String cloToJava(String type){
Configuration config = getConfig();
return config.getString(type,null);
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCapitalizeCamelCase(String s) {
if (s == null) {
return null;
}
s = toCamelCase(s);
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
}
在 util 包下创建代码生成工具类 GeneratorUtil,该类用于将获取到的Mysql字段信息转出Java字段类型,并且获取代码生成的路径,读取 Template,并且输出成文件,代码如下:
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import java.io.*;
import java.time.LocalDate;
import java.util.*;
/**
* 代码生成
*
* @author jie
* @date 2019-01-02
*/
@Slf4j
public class GeneratorUtil {
private static final String TIMESTAMP = "Timestamp";
private static final String BIGDECIMAL = "BigDecimal";
private static final String PK = "PRI";
private static final String EXTRA = "auto_increment";
/**
* 生成代码
* @param columnInfos
* @param pack
* @param author
* @param tableName
* @throws IOException
*/
public static void generatorCode(List<ColumnInfo> columnInfos, String pack, String author, String tableName) throws IOException {
Map<String, Object> map = new HashMap<>();
map.put("package", pack);
map.put("author", author);
map.put("date", LocalDate.now().toString());
map.put("tableName", tableName);
// 转换为小写开头的的类名, hello_world == helloWorld
String className = ColumnUtil.toCapitalizeCamelCase(tableName);
// 转换为大写开头的类名, hello_world == HelloWorld
String changeClassName = ColumnUtil.toCamelCase(tableName);
map.put("className", className);
map.put("changeClassName", changeClassName);
// 是否包含 Timestamp 类型
map.put("hasTimestamp", false);
// 是否包含 BigDecimal 类型
map.put("hasBigDecimal", false);
// 是否为自增主键
map.put("auto", false);
List<Map<String, Object>> columns = new ArrayList<>();
for (ColumnInfo column : columnInfos) {
Map<String, Object> listMap = new HashMap<>();
listMap.put("columnComment", column.getColumnComment());
listMap.put("columnKey", column.getColumnKey());
String colType = ColumnUtil.cloToJava(column.getColumnType().toString());
String changeColumnName = ColumnUtil.toCamelCase(column.getColumnName().toString());
if (PK.equals(column.getColumnKey())) {
map.put("pkColumnType", colType);
map.put("pkChangeColName", changeColumnName);
}
if (TIMESTAMP.equals(colType)) {
map.put("hasTimestamp", true);
}
if (BIGDECIMAL.equals(colType)) {
map.put("hasBigDecimal", true);
}
if (EXTRA.equals(column.getExtra())) {
map.put("auto", true);
}
listMap.put("columnType", colType);
listMap.put("columnName", column.getColumnName());
listMap.put("isNullable", column.getIsNullable());
listMap.put("changeColumnName", changeColumnName);
columns.add(listMap);
}
map.put("columns", columns);
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
configuration.setClassForTemplateLoading(GeneratorUtil.class, "/template");
Template template = configuration.getTemplate("Entity.ftl");
// 获取文件路径
String filePath = getAdminFilePath(pack, className);
File file = new File(filePath);
// 生成代码
genFile(file, template, map);
}
/**
* 定义文件路径以及名称
*/
private static String getAdminFilePath(String pack, String className) {
String ProjectPath = System.getProperty("user.dir") + File.separator;
String packagePath = ProjectPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
if (!ObjectUtils.isEmpty(pack)) {
packagePath += pack.replace(".", File.separator) + File.separator;
}
return packagePath + "entity" + File.separator + className + ".java";
}
private static void genFile(File file, Template template, Map<String, Object> params) throws IOException {
File parentFile = file.getParentFile();
// 创建目录
if (null != parentFile && !parentFile.exists()) {
parentFile.mkdirs();
}
//创建输出流
Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
//输出模板和数据模型都对应的文件
try {
template.process(params, writer);
} catch (TemplateException e) {
e.printStackTrace();
}
}
}
在 resources 的 template 目录下创建 framework 模板 Entity.ftl,代码如下:
package ${package}.entity;
import lombok.Data;
import javax.persistence.*;
<#if hasTimestamp>
import java.sql.Timestamp;
</#if>
<#if hasBigDecimal>
import java.math.BigDecimal;
</#if>
import java.io.Serializable;
/**
* @author ${author}
* @date ${date}
*/
@Entity
@Data
@Table(name="${tableName}")
public class ${className} implements Serializable {
<#if columns??>
<#list columns as column>
<#if column.columnComment != ''>
// ${column.columnComment}
</#if>
<#if column.columnKey = 'PRI'>
@Id
<#if auto>
@GeneratedValue(strategy = GenerationType.IDENTITY)
</#if>
</#if>
@Column(name = "${column.columnName}"<#if column.columnKey = 'UNI'>,unique = true</#if><#if column.isNullable = 'NO' && column.columnKey != 'PRI'>,nullable = false</#if>)
private ${column.columnType} ${column.changeColumnName};
</#list>
</#if>
}
创建服务类 GeneratorService,该类用于获取数据库表的源数据
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 代码生成服务
*/
@Service
public class GeneratorService {
@PersistenceContext
private EntityManager em;
public List<ColumnInfo> getColumns(String tableName) {
StringBuilder sql = new StringBuilder("select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns where ");
if(!ObjectUtils.isEmpty(tableName)){
sql.append("table_name = '").append(tableName).append("' ");
}
sql.append("and table_schema = (select database()) order by ordinal_position");
Query query = em.createNativeQuery(sql.toString());
List result = query.getResultList();
List<ColumnInfo> columnInfos = new ArrayList<>();
for (Object o : result) {
Object[] obj = (Object[])o;
columnInfos.add(new ColumnInfo(obj[0],obj[1],obj[2],obj[3],obj[4],obj[5]));
}
return columnInfos;
}
}
由于没有前端页面,所以只能在测试类中演示代码生成功能,GeneratorDomeApplicationTests 修改如下
import com.ydyno.util.GeneratorUtil;
import com.ydyno.vo.ColumnInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.List;
@SpringBootTest
class GeneratorDomeApplicationTests {
@Autowired
private GeneratorService generatorService;
@Test
void genTest() throws IOException {
String tableName = "job";
String pack = "com.ydyno";
String author = "Zheng Jie";
List<ColumnInfo> columnInfos = generatorService.getColumns(tableName);
GeneratorUtil.generatorCode(columnInfos,pack,author,tableName);
}
}
执行后,查看创建好的Entity
猜你还喜欢
- 03-29 [编程相关] Winform窗体圆角以及描边完美解决方案
- 03-29 [前端问题] has been blocked by CORS policy跨域问题解决
- 03-29 [编程相关] GitHub Actions 入门教程
- 03-29 [编程探讨] CSS Grid 网格布局教程
- 10-12 [编程相关] python实现文件夹所有文件编码从GBK转为UTF8
- 10-11 [编程算法] opencv之霍夫变换:圆
- 10-11 [编程算法] OpenCV Camshift算法+目标跟踪源码
- 10-11 [Python] python 创建 Telnet 客户端
- 10-11 [编程相关] Python 基于 Yolov8 + CPU 实现物体检测
- 03-15 [脚本工具] 使用go语言开发自动化脚本 - 一键定场、抢购、预约、捡漏
- 01-08 [编程技术] 秒杀面试官系列 - Redis zset底层是怎么实现的
- 01-05 [编程技术] 《Redis设计与实现》pdf
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[小说] 【飞卢小说】2100+本合集
[PPT模板] 160套收费高质量毕业答辩PPT模板
[小说] 付费小说300+
[文章合集] 知乎盐选付费文章合集,2888部
[书籍] 《高分图书50部》好书共读 休闲 成长 生活一个都不能少[epub]
[游戏娱乐] 《我的梦想卧室》v20240413中文版
[资料] 知识星球 付费课程(精选130篇)[PDF]
[有声读物] 冯唐讲《资治通鉴》(完结)【MP3】
[电影] 2022年国产奇幻片《聊斋新传之画皮人》HD国语中字
[书籍] 国学书籍70本(绝版珍藏)【TXT】
[资料] [大学期末救急课] 猴博士+高斯课堂+斐多课堂,全集视频合集
[云资源] 价值2万元的老男孩Python教程
[书库] 史上最全摄影书推荐(附700本PDF版打包下载)
[云资源] 花了一千多元买的私人健身教程
[下载工具] Internet Download Manager 6.42.7 (IDM)
[影视] 灌篮高手 WEB-DL版下载/Slam Dunk/スラムダンク/灌篮高手:THE FIRST/灌篮高手电影版 2022 The First Slam Dunk 61.35G
[即时通讯] 腾讯QQ PC版9.7.22.29315去广告绿色纯净版
[开发环境] PhpStorm2023中文激活版v2023.3.3 正式版
[资料] 3000 套电影电视剧 LOGO 宣传片常用音效合集包
[安卓软件] 酷我音乐APP_v10.7.6.4 去广告破解豪华VIP版
[云资源] 价值2万元的老男孩Python教程
[影视] 灌篮高手 WEB-DL版下载/Slam Dunk/スラムダンク/灌篮高手:THE FIRST/灌篮高手电影版 2022 The First Slam Dunk 61.35G
[云资源] 花了一千多元买的私人健身教程
[书库] 史上最全摄影书推荐(附700本PDF版打包下载)
[动画] 北斗神拳(1984) [两季合集] [MKV]
[资料] 抗战阵亡将士资料+续编
[电视剧] 三体 (2024) 全8集 网飞版本 中文字幕 合集
[影视] 三大队 WEB-DL版下载/Endless Journey/请转告局长,三大队任务完成了 2023 三大队 6.7G
[纪录片] 河西走廊【10集 国语 中文字幕 1080P 10.8G MP4】
[安卓软件] OfficeSuite中文版APP v14.2.50872.0破解版
- 最新评论
-
好东西阿zfy123123 评论于:04-18 谢谢楼主xiaoqi 评论于:04-12 勿在线解压,勿手机解压,请在电脑上用最新款压缩软件解压!推荐360压缩或者好压CC下载站 评论于:04-10 无法解压啊,客服能不能给个解压教程ravengrey 评论于:04-10 谢谢支持!!CC下载站 评论于:03-26 很棒的资源,感谢分享云体风身 评论于:03-26 感谢分享,好东西云体风身 评论于:03-26 谢谢支持!CC下载站 评论于:03-14 央视精品,感谢付出提供。qwer9009 评论于:03-14 谢谢支持!!!CC下载站 评论于:03-13
- 热门tag