Browse Source

spring-boot-demo-codegen 完成

pull/1/head
Yangkai.Shen 6 years ago
parent
commit
f3a927461b
3 changed files with 420 additions and 3 deletions
  1. +414
    -1
      spring-boot-demo-codegen/README.md
  2. +1
    -1
      spring-boot-demo-codegen/src/main/java/com/xkcoding/codegen/constants/GenConstants.java
  3. +5
    -1
      spring-boot-demo-codegen/src/main/resources/static/index.html

+ 414
- 1
spring-boot-demo-codegen/README.md View File

@@ -1,3 +1,416 @@
# spring-boot-demo-codegen

> 此 demo 主要演示了 Spring Boot 使用模板生成代码,并提供前端页面,可生成 Entity/Mapper/Service/Controller 等代码。
> 此 demo 主要演示了 Spring Boot 使用**模板技术**生成代码,并提供前端页面,可生成 Entity/Mapper/Service/Controller 等代码。

## 1. 主要功能

1. 使用 `velocity` 代码生成
2. 暂时支持mysql数据库的代码生成
3. 提供前端页面展示,并下载代码压缩包

> 注意:① Entity里使用lombok,简化代码 ② Mapper 和 Service 层集成 Mybatis-Plus 简化代码

## 2. 运行

1. 运行 `SpringBootDemoCodegenApplication` 启动项目
2. 打开浏览器,输入 http://localhost:8080/demo/index.html
3. 输入查询条件,生成代码

## 3. 关键代码

### 3.1. pom.xml

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-boot-demo-codegen</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-demo-codegen</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>com.xkcoding</groupId>
<artifactId>spring-boot-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>

<!--velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-demo-codegen</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
```

### 3.2. 代码生成器配置

```properties
#代码生成器,配置信息
mainPath=com.xkcoding
#包名
package=com.xkcoding
moduleName=generator
#作者
author=Yangkai.Shen
#表前缀(类名不会包含表前缀)
tablePrefix=tb_
#类型转换,配置信息
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=LocalDateTime
datetime=LocalDateTime
timestamp=LocalDateTime
```

### 3.3. CodeGenUtil.java

```java
/**
* <p>
* 代码生成器 工具类
* </p>
*
* @package: com.xkcoding.codegen.utils
* @description: 代码生成器 工具类
* @author: yangkai.shen
* @date: Created in 2019-03-22 09:27
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Slf4j
@UtilityClass
public class CodeGenUtil {

private final String ENTITY_JAVA_VM = "Entity.java.vm";
private final String MAPPER_JAVA_VM = "Mapper.java.vm";
private final String SERVICE_JAVA_VM = "Service.java.vm";
private final String SERVICE_IMPL_JAVA_VM = "ServiceImpl.java.vm";
private final String CONTROLLER_JAVA_VM = "Controller.java.vm";
private final String MAPPER_XML_VM = "Mapper.xml.vm";
private final String API_JS_VM = "api.js.vm";

private List<String> getTemplates() {
List<String> templates = new ArrayList<>();
templates.add("template/Entity.java.vm");
templates.add("template/Mapper.java.vm");
templates.add("template/Mapper.xml.vm");
templates.add("template/Service.java.vm");
templates.add("template/ServiceImpl.java.vm");
templates.add("template/Controller.java.vm");

templates.add("template/api.js.vm");
return templates;
}

/**
* 生成代码
*/
public void generatorCode(GenConfig genConfig, Entity table, List<Entity> columns, ZipOutputStream zip) {
//配置信息
Props props = getConfig();
boolean hasBigDecimal = false;
//表信息
TableEntity tableEntity = new TableEntity();
tableEntity.setTableName(table.getStr("tableName"));

if (StrUtil.isNotBlank(genConfig.getComments())) {
tableEntity.setComments(genConfig.getComments());
} else {
tableEntity.setComments(table.getStr("tableComment"));
}

String tablePrefix;
if (StrUtil.isNotBlank(genConfig.getTablePrefix())) {
tablePrefix = genConfig.getTablePrefix();
} else {
tablePrefix = props.getStr("tablePrefix");
}

//表名转换成Java类名
String className = tableToJava(tableEntity.getTableName(), tablePrefix);
tableEntity.setCaseClassName(className);
tableEntity.setLowerClassName(StrUtil.lowerFirst(className));

//列信息
List<ColumnEntity> columnList = Lists.newArrayList();
for (Entity column : columns) {
ColumnEntity columnEntity = new ColumnEntity();
columnEntity.setColumnName(column.getStr("columnName"));
columnEntity.setDataType(column.getStr("dataType"));
columnEntity.setComments(column.getStr("columnComment"));
columnEntity.setExtra(column.getStr("extra"));

//列名转换成Java属性名
String attrName = columnToJava(columnEntity.getColumnName());
columnEntity.setCaseAttrName(attrName);
columnEntity.setLowerAttrName(StrUtil.lowerFirst(attrName));

//列的数据类型,转换成Java类型
String attrType = props.getStr(columnEntity.getDataType(), "unknownType");
columnEntity.setAttrType(attrType);
if (!hasBigDecimal && "BigDecimal".equals(attrType)) {
hasBigDecimal = true;
}
//是否主键
if ("PRI".equalsIgnoreCase(column.getStr("columnKey")) && tableEntity.getPk() == null) {
tableEntity.setPk(columnEntity);
}

columnList.add(columnEntity);
}
tableEntity.setColumns(columnList);

//没主键,则第一个字段为主键
if (tableEntity.getPk() == null) {
tableEntity.setPk(tableEntity.getColumns().get(0));
}

//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>(16);
map.put("tableName", tableEntity.getTableName());
map.put("pk", tableEntity.getPk());
map.put("className", tableEntity.getCaseClassName());
map.put("classname", tableEntity.getLowerClassName());
map.put("pathName", tableEntity.getLowerClassName().toLowerCase());
map.put("columns", tableEntity.getColumns());
map.put("hasBigDecimal", hasBigDecimal);
map.put("datetime", DateUtil.now());
map.put("year", DateUtil.year(new Date()));

if (StrUtil.isNotBlank(genConfig.getComments())) {
map.put("comments", genConfig.getComments());
} else {
map.put("comments", tableEntity.getComments());
}

if (StrUtil.isNotBlank(genConfig.getAuthor())) {
map.put("author", genConfig.getAuthor());
} else {
map.put("author", props.getStr("author"));
}

if (StrUtil.isNotBlank(genConfig.getModuleName())) {
map.put("moduleName", genConfig.getModuleName());
} else {
map.put("moduleName", props.getStr("moduleName"));
}

if (StrUtil.isNotBlank(genConfig.getPackageName())) {
map.put("package", genConfig.getPackageName());
map.put("mainPath", genConfig.getPackageName());
} else {
map.put("package", props.getStr("package"));
map.put("mainPath", props.getStr("mainPath"));
}
VelocityContext context = new VelocityContext(map);

//获取模板列表
List<String> templates = getTemplates();
for (String template : templates) {
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, CharsetUtil.UTF_8);
tpl.merge(context, sw);

try {
//添加到zip
zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template, tableEntity.getCaseClassName(), map
.get("package")
.toString(), map.get("moduleName").toString()))));
IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
IoUtil.close(sw);
zip.closeEntry();
} catch (IOException e) {
throw new RuntimeException("渲染模板失败,表名:" + tableEntity.getTableName(), e);
}
}
}


/**
* 列名转换成Java属性名
*/
private String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}

/**
* 表名转换成Java类名
*/
private String tableToJava(String tableName, String tablePrefix) {
if (StrUtil.isNotBlank(tablePrefix)) {
tableName = tableName.replaceFirst(tablePrefix, "");
}
return columnToJava(tableName);
}

/**
* 获取配置信息
*/
private Props getConfig() {
Props props = new Props("generator.properties");
props.autoLoad(true);
return props;
}

/**
* 获取文件名
*/
private String getFileName(String template, String className, String packageName, String moduleName) {
// 包路径
String packagePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
// 资源路径
String resourcePath = GenConstants.SIGNATURE + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
// api路径
String apiPath = GenConstants.SIGNATURE + File.separator + "api" + File.separator;

if (StrUtil.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
}

if (template.contains(ENTITY_JAVA_VM)) {
return packagePath + "entity" + File.separator + className + ".java";
}

if (template.contains(MAPPER_JAVA_VM)) {
return packagePath + "mapper" + File.separator + className + "Mapper.java";
}

if (template.contains(SERVICE_JAVA_VM)) {
return packagePath + "service" + File.separator + className + "Service.java";
}

if (template.contains(SERVICE_IMPL_JAVA_VM)) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}

if (template.contains(CONTROLLER_JAVA_VM)) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}

if (template.contains(MAPPER_XML_VM)) {
return resourcePath + "mapper" + File.separator + className + "Mapper.xml";
}

if (template.contains(API_JS_VM)) {
return apiPath + className.toLowerCase() + ".js";
}

return null;
}
}
```

### 3.4. 其余代码参见demo

## 4. 演示

<video id="video" controls="" preload="none">
<source id="mp4" src="https://static.xkcoding.com/code/spring-boot-demo/codegen/codegen.mp4" type="video/mp4">
<p>您的浏览器版本过低,不支持播放视频演示</p>
</video>

## 5. 参考

- [基于人人开源 自动构建项目_V1](https://qq343509740.gitee.io/2018/12/20/%E7%AC%94%E8%AE%B0/%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE/%E5%9F%BA%E4%BA%8E%E4%BA%BA%E4%BA%BA%E5%BC%80%E6%BA%90%20%E8%87%AA%E5%8A%A8%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE_V1/)

- [Mybatis-Plus代码生成器](https://mybatis.plus/guide/generator.html#%E6%B7%BB%E5%8A%A0%E4%BE%9D%E8%B5%96)


+ 1
- 1
spring-boot-demo-codegen/src/main/java/com/xkcoding/codegen/constants/GenConstants.java View File

@@ -17,5 +17,5 @@ public interface GenConstants {
/**
* 签名
*/
String SIGNATURE = "xkcoding";
String SIGNATURE = "xkcoding代码生成";
}

+ 5
- 1
spring-boot-demo-codegen/src/main/resources/static/index.html View File

@@ -280,7 +280,11 @@
search(name) {
this.$refs[name].validate((valid) => {
if (this.tableRequest.prepend === "") {
this.$Message.error("请选择前缀");
this.$Message.error("请选择jdbc-url前缀");
return
}
if (this.tableRequest.prepend !== "jdbc:mysql://") {
this.$Message.error("暂时只支持 mysql 类型");
return
}
if (valid) {


Loading…
Cancel
Save