From ff2f3144dcbf718094a82a9e3acf112ede033781 Mon Sep 17 00:00:00 2001
From: bunny <1319900154@qq.com>
Date: Tue, 22 Apr 2025 19:17:25 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=A7=A3=E6=9E=90SQL?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
generator-code/pom.xml | 7 +-
.../bunny/controller/SqlParserController.java | 38 ++++++
.../java/cn/bunny/core/DatabaseInfoCore.java | 109 +++++++++++++++++
.../java/cn/bunny/core/SqlParserCore.java | 110 ++++++++++++++++++
.../bunny/core/vms/AbstractVmsGenerator.java | 62 ++++++++++
.../vms/VmsArgumentDtoBaseVmsGenerator.java | 84 +++++++++++++
.../java/cn/bunny/dao/dto/VmsArgumentDto.java | 16 ++-
.../cn/bunny/service/SqlParserService.java | 13 +++
.../java/cn/bunny/service/VmsService.java | 2 +
.../service/impl/SqlParserServiceImpl.java | 27 +++++
.../bunny/service/impl/TableServiceImpl.java | 100 +---------------
.../cn/bunny/service/impl/VmsServiceImpl.java | 30 +++--
.../src/main/java/cn/bunny/utils/VmsUtil.java | 70 -----------
.../src/test/java/cn/bunny/SqlParserTest.java | 105 +++++++++++++++++
14 files changed, 589 insertions(+), 184 deletions(-)
create mode 100644 generator-code/src/main/java/cn/bunny/controller/SqlParserController.java
create mode 100644 generator-code/src/main/java/cn/bunny/core/SqlParserCore.java
create mode 100644 generator-code/src/main/java/cn/bunny/core/vms/AbstractVmsGenerator.java
create mode 100644 generator-code/src/main/java/cn/bunny/core/vms/VmsArgumentDtoBaseVmsGenerator.java
create mode 100644 generator-code/src/main/java/cn/bunny/service/SqlParserService.java
create mode 100644 generator-code/src/main/java/cn/bunny/service/impl/SqlParserServiceImpl.java
create mode 100644 generator-code/src/test/java/cn/bunny/SqlParserTest.java
diff --git a/generator-code/pom.xml b/generator-code/pom.xml
index 09992f8..b787906 100644
--- a/generator-code/pom.xml
+++ b/generator-code/pom.xml
@@ -64,6 +64,11 @@
org.springframework.boot
spring-boot-starter-jdbc
+
+ com.github.jsqlparser
+ jsqlparser
+ 5.1
+
@@ -74,7 +79,7 @@
cn.hutool
hutool-all
-
+
com.github.xiaoymin
diff --git a/generator-code/src/main/java/cn/bunny/controller/SqlParserController.java b/generator-code/src/main/java/cn/bunny/controller/SqlParserController.java
new file mode 100644
index 0000000..6441078
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/controller/SqlParserController.java
@@ -0,0 +1,38 @@
+package cn.bunny.controller;
+
+import cn.bunny.core.SqlParserCore;
+import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.result.Result;
+import cn.bunny.dao.vo.TableInfoVo;
+import cn.bunny.service.SqlParserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Tag(name = "解析SQL", description = "解析SQL接口")
+@RestController
+@RequestMapping("/api/sqlParser")
+public class SqlParserController {
+
+ @Resource
+ private SqlParserService sqlParserService;
+
+ @Operation(summary = "解析SQL成表信息", description = "解析SQL成表信息")
+ @PostMapping("tableInfo")
+ public Result tableInfo(String sql) {
+ TableInfoVo vo = sqlParserService.tableInfo(sql);
+ return Result.success(vo);
+ }
+
+ @Operation(summary = "解析SQL成列数据", description = "解析SQL成列数据")
+ @PostMapping("columnMetaData")
+ public Result> columnMetaData(String sql) {
+ List vo = SqlParserCore.parserColumnInfo(sql);
+ return Result.success(vo);
+ }
+}
diff --git a/generator-code/src/main/java/cn/bunny/core/DatabaseInfoCore.java b/generator-code/src/main/java/cn/bunny/core/DatabaseInfoCore.java
index 4a11b02..58c7789 100644
--- a/generator-code/src/main/java/cn/bunny/core/DatabaseInfoCore.java
+++ b/generator-code/src/main/java/cn/bunny/core/DatabaseInfoCore.java
@@ -1,15 +1,20 @@
package cn.bunny.core;
+import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.entity.DatabaseInfoMetaData;
import cn.bunny.dao.entity.TableMetaData;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/* 数据库信息内容 */
@@ -84,4 +89,108 @@ public class DatabaseInfoCore {
return tableMetaData;
}
}
+
+ /**
+ * 获取[当前/所有]数据库表
+ *
+ * @return 所有表信息
+ */
+ @SneakyThrows
+ public List databaseTableList(String dbName) {
+ // 当前数据库数据库所有的表
+ List allTableInfo = new ArrayList<>();
+
+ try (Connection connection = dataSource.getConnection()) {
+ DatabaseMetaData metaData = connection.getMetaData();
+
+ // 当前数据库中所有的表
+ ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
+
+ while (tables.next()) {
+ // 表名称
+ dbName = tables.getString("TABLE_NAME");
+
+ // 设置表信息
+ TableMetaData tableMetaData = tableInfoMetaData(dbName);
+
+ allTableInfo.add(tableMetaData);
+ }
+ }
+
+ return allTableInfo;
+ }
+
+ /**
+ * 获取当前表的列属性
+ *
+ * @param tableName 表名称
+ * @return 当前表所有的列内容
+ */
+ @SneakyThrows
+ public List tableColumnInfo(String tableName) {
+ try (Connection connection = dataSource.getConnection()) {
+ DatabaseMetaData metaData = connection.getMetaData();
+ List columns = new ArrayList<>();
+ // 当前表的主键
+ Set primaryKeyColumns = getPrimaryKeyColumns(tableName);
+
+ // 当前表的列信息
+ try (ResultSet columnsRs = metaData.getColumns(null, null, tableName, null)) {
+ while (columnsRs.next()) {
+ ColumnMetaData column = new ColumnMetaData();
+ // 列字段
+ String columnName = columnsRs.getString("COLUMN_NAME");
+ // 将当前表的列类型转成 Java 类型
+ String javaType = TypeConvertCore.convertToJavaType(column.getJdbcType());
+
+ // 设置列字段
+ column.setColumnName(columnName);
+ // 列字段转成 下划线 -> 小驼峰
+ column.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
+ // 列字段转成 下划线 -> 大驼峰名称
+ column.setUppercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName(), true));
+ // 字段类型
+ column.setJdbcType(columnsRs.getString("TYPE_NAME"));
+ // 字段类型转 Java 类型
+ column.setJavaType(javaType);
+ // 字段类型转 JavaScript 类型
+ column.setJavascriptType(StringUtils.uncapitalize(javaType));
+ // 备注信息
+ column.setComment(columnsRs.getString("REMARKS"));
+
+ // 确保 primaryKeyColumns 不为空
+ if (!primaryKeyColumns.isEmpty()) {
+ // 是否是主键
+ boolean isPrimaryKey = primaryKeyColumns.contains(columnName);
+ column.setIsPrimaryKey(isPrimaryKey);
+ }
+ columns.add(column);
+ }
+ }
+
+ columns.get(0).setIsPrimaryKey(true);
+ return columns;
+ }
+ }
+
+ /**
+ * 数据库所有的信息
+ *
+ * @return 当前连接的数据库信息属性
+ */
+ @SneakyThrows
+ public DatabaseInfoMetaData databaseInfoMetaData() {
+ try (Connection connection = dataSource.getConnection()) {
+ DatabaseMetaData metaData = connection.getMetaData();
+
+ return DatabaseInfoMetaData.builder()
+ .databaseProductName(metaData.getDatabaseProductName())
+ .databaseProductVersion(metaData.getDatabaseProductVersion())
+ .driverName(metaData.getDriverName())
+ .driverVersion(metaData.getDriverVersion())
+ .url(metaData.getURL())
+ .username(metaData.getUserName())
+ .build();
+ }
+ }
}
diff --git a/generator-code/src/main/java/cn/bunny/core/SqlParserCore.java b/generator-code/src/main/java/cn/bunny/core/SqlParserCore.java
new file mode 100644
index 0000000..43a914d
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/core/SqlParserCore.java
@@ -0,0 +1,110 @@
+package cn.bunny.core;
+
+import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.entity.TableMetaData;
+import lombok.SneakyThrows;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.create.table.CreateTable;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SqlParserCore {
+
+ /**
+ * 解析 sql 表信息
+ *
+ * @param sql sql字符串
+ * @return 表西悉尼
+ */
+ @SneakyThrows
+ public static TableMetaData parserTableInfo(String sql) {
+ TableMetaData tableInfo = new TableMetaData();
+
+ // 解析sql
+ Statement statement;
+ try {
+ statement = CCJSqlParserUtil.parse(sql);
+ } catch (JSQLParserException e) {
+ throw new RuntimeException("SQL解析失败");
+ }
+ if (!(statement instanceof CreateTable createTable)) {
+ throw new IllegalArgumentException("缺少SQL语句");
+ }
+
+ // 设置表基本信息
+ String tableName = createTable.getTable().getName().replaceAll("`", "");
+ tableInfo.setTableName(tableName);
+ tableInfo.setTableType("TABLE");
+ String tableOptionsStrings = String.join(" ", createTable.getTableOptionsStrings());
+
+ // 注释信息
+ Pattern pattern = Pattern.compile("COMMENT\\s*=\\s*'(.*?)'", Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(tableOptionsStrings);
+ if (matcher.find()) {
+ tableInfo.setComment(matcher.group(1));
+ }
+
+ return tableInfo;
+ }
+
+ /**
+ * 解析 sql 列信息
+ *
+ * @param sql sql字符串
+ * @return 列属性列表
+ */
+ @SneakyThrows
+ public static List parserColumnInfo(String sql) {
+ // 解析sql
+ Statement statement;
+ try {
+ statement = CCJSqlParserUtil.parse(sql);
+ } catch (JSQLParserException e) {
+ throw new RuntimeException("SQL解析失败");
+ }
+
+ if (!(statement instanceof CreateTable createTable)) {
+ throw new IllegalArgumentException("缺少SQL语句");
+ }
+
+ return createTable.getColumnDefinitions()
+ .stream().map(column -> {
+ // 列信息
+ ColumnMetaData columnInfo = new ColumnMetaData();
+
+ // 列名称
+ columnInfo.setColumnName(column.getColumnName());
+
+ // 设置 JDBC 类型
+ String dataType = column.getColDataType().getDataType();
+ columnInfo.setJdbcType(dataType);
+
+ // 设置 Java 类型
+ String javaType = TypeConvertCore.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
+ columnInfo.setJavaType(javaType);
+
+ // 设置 JavaScript 类型
+ columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType));
+
+ // 列字段转成 下划线 -> 小驼峰
+ columnInfo.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
+ // 列字段转成 下划线 -> 大驼峰名称
+ columnInfo.setUppercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName(), true));
+
+ // 解析注释
+ List columnSpecs = column.getColumnSpecs();
+ String columnSpecsString = String.join(" ", columnSpecs);
+ Matcher columnSpecsStringMatcher = Pattern.compile("COMMENT\\s*'(.*?)'", Pattern.CASE_INSENSITIVE).matcher(columnSpecsString);
+ if (columnSpecsStringMatcher.find()) {
+ columnInfo.setComment(columnSpecsStringMatcher.group(1));
+ }
+
+ return columnInfo;
+ }).toList();
+ }
+}
diff --git a/generator-code/src/main/java/cn/bunny/core/vms/AbstractVmsGenerator.java b/generator-code/src/main/java/cn/bunny/core/vms/AbstractVmsGenerator.java
new file mode 100644
index 0000000..ae67134
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/core/vms/AbstractVmsGenerator.java
@@ -0,0 +1,62 @@
+package cn.bunny.core.vms;
+
+import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.entity.TableMetaData;
+import org.apache.velocity.VelocityContext;
+
+import java.io.StringWriter;
+import java.util.List;
+
+/**
+ * 模板方法模式
+ * 如果需要继承 AbstractVmsGenerator
+ */
+public abstract class AbstractVmsGenerator {
+
+ /**
+ * 添加生成内容
+ */
+ abstract void addContext(VelocityContext context);
+
+ /**
+ * Velocity 生成模板
+ *
+ * @param context
+ * @param writer StringWriter 写入
+ */
+ abstract void templateMerge(VelocityContext context, StringWriter writer);
+
+ /**
+ * 生成模板
+ *
+ * @param tableMetaData 表属性
+ * @param columnInfoList 列属性数组
+ * @return StringWriter
+ */
+ public final StringWriter generatorCodeTemplate(TableMetaData tableMetaData, List columnInfoList) {
+ VelocityContext context = new VelocityContext();
+
+ // 添加要生成的属性
+ StringWriter writer = new StringWriter();
+ List list = columnInfoList.stream().map(ColumnMetaData::getColumnName).toList();
+
+ // vm 不能直接写 `{` 需要转换下
+ context.put("leftBrace", "{");
+
+ // 当前的表名
+ context.put("tableName", tableMetaData.getTableName());
+
+ // 当前表的列信息
+ context.put("columnInfoList", columnInfoList);
+
+ // 数据库sql列
+ context.put("baseColumnList", String.join(",", list));
+
+ // 添加需要生成的内容
+ addContext(context);
+
+ templateMerge(context, writer);
+
+ return writer;
+ }
+}
diff --git a/generator-code/src/main/java/cn/bunny/core/vms/VmsArgumentDtoBaseVmsGenerator.java b/generator-code/src/main/java/cn/bunny/core/vms/VmsArgumentDtoBaseVmsGenerator.java
new file mode 100644
index 0000000..4165fb7
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/core/vms/VmsArgumentDtoBaseVmsGenerator.java
@@ -0,0 +1,84 @@
+package cn.bunny.core.vms;
+
+import cn.bunny.core.TypeConvertCore;
+import cn.bunny.dao.dto.VmsArgumentDto;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 使用模板方法,方便扩展
+ * 如果需要继承 AbstractVmsGenerator
+ */
+public class VmsArgumentDtoBaseVmsGenerator extends AbstractVmsGenerator {
+
+ private final VmsArgumentDto dto;
+ private final String path;
+
+ /**
+ * @param dto 类名称可以自定义,格式为 xxx_xxx
+ * @param path 当前路径
+ */
+ public VmsArgumentDtoBaseVmsGenerator(VmsArgumentDto dto, String path) {
+ this.dto = dto;
+ this.path = path;
+ }
+
+ /**
+ * 添加生成内容
+ */
+ @Override
+ void addContext(VelocityContext context) {
+ // 当前日期
+ String date = new SimpleDateFormat(dto.getSimpleDateFormat()).format(new Date());
+ context.put("date", date);
+
+ // 作者名字
+ context.put("author", dto.getAuthor());
+
+ // 每个 Controller 上的请求前缀
+ context.put("requestMapping", dto.getRequestMapping());
+
+ // 类名称如果是小驼峰,需要 [手写] 为 [下划线] 之后由 [代码 -> 小驼峰/大驼峰]
+ String className = dto.getClassName();
+
+ // 去除表开头前缀
+ String tablePrefixes = dto.getTablePrefixes();
+
+ // 表字段的注释内容
+ context.put("comment", dto.getComment());
+
+ // 设置包名称
+ context.put("package", dto.getPackageName());
+
+ // 将 表前缀 转成数组
+ String replaceTableName = "";
+ for (String prefix : tablePrefixes.split("[,,]")) {
+ replaceTableName = className.replace(prefix, "");
+ }
+
+ // 将类名称转成小驼峰
+ String toCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName);
+ context.put("classLowercaseName", toCamelCase);
+
+ // 将类名称转成大驼峰
+ String convertToCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName, true);
+ context.put("classUppercaseName", convertToCamelCase);
+ }
+
+ /**
+ * Velocity 生成模板
+ *
+ * @param writer StringWriter 写入
+ */
+ @Override
+ void templateMerge(VelocityContext context, StringWriter writer) {
+ // Velocity 生成模板
+ Template servicePathTemplate = Velocity.getTemplate("vms/" + path, "UTF-8");
+ servicePathTemplate.merge(context, writer);
+ }
+}
diff --git a/generator-code/src/main/java/cn/bunny/dao/dto/VmsArgumentDto.java b/generator-code/src/main/java/cn/bunny/dao/dto/VmsArgumentDto.java
index adbaf4a..d096a44 100644
--- a/generator-code/src/main/java/cn/bunny/dao/dto/VmsArgumentDto.java
+++ b/generator-code/src/main/java/cn/bunny/dao/dto/VmsArgumentDto.java
@@ -26,15 +26,15 @@ public class VmsArgumentDto {
String requestMapping = "/api";
/* 类名称,格式为:xxx xxx_xxx */
- @NotBlank(message = "类名称不能为空" )
- @NotNull(message = "类名称不能为空" )
- @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$" , message = "类名称不合法" )
+ @NotBlank(message = "类名称不能为空")
+ @NotNull(message = "类名称不能为空")
+ @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$", message = "类名称不合法")
private String className;
/* 表名称 */
- @NotBlank(message = "表名称不能为空" )
- @NotNull(message = "表名称不能为空" )
- @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$" , message = "表名称不合法" )
+ @NotBlank(message = "表名称不能为空")
+ @NotNull(message = "表名称不能为空")
+ @Pattern(regexp = "^(?:[a-z][a-z0-9_]*|[_a-z][a-z0-9_]*)$", message = "表名称不合法")
private String tableName;
/* 时间格式 */
@@ -48,4 +48,8 @@ public class VmsArgumentDto {
/* 路径 */
private List path;
+
+ /* SQL 语句 */
+ private String sql;
}
+
diff --git a/generator-code/src/main/java/cn/bunny/service/SqlParserService.java b/generator-code/src/main/java/cn/bunny/service/SqlParserService.java
new file mode 100644
index 0000000..ba63a2f
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/service/SqlParserService.java
@@ -0,0 +1,13 @@
+package cn.bunny.service;
+
+import cn.bunny.dao.vo.TableInfoVo;
+
+public interface SqlParserService {
+ /**
+ * 解析SQL内容
+ *
+ * @param sql Sql语句
+ * @return 表信息内容
+ */
+ TableInfoVo tableInfo(String sql);
+}
diff --git a/generator-code/src/main/java/cn/bunny/service/VmsService.java b/generator-code/src/main/java/cn/bunny/service/VmsService.java
index 8bb1b91..12d9c1b 100644
--- a/generator-code/src/main/java/cn/bunny/service/VmsService.java
+++ b/generator-code/src/main/java/cn/bunny/service/VmsService.java
@@ -32,4 +32,6 @@ public interface VmsService {
* @return zip 文件
*/
ResponseEntity downloadByZip(@Valid VmsArgumentDto dto);
+
+
}
diff --git a/generator-code/src/main/java/cn/bunny/service/impl/SqlParserServiceImpl.java b/generator-code/src/main/java/cn/bunny/service/impl/SqlParserServiceImpl.java
new file mode 100644
index 0000000..72378f4
--- /dev/null
+++ b/generator-code/src/main/java/cn/bunny/service/impl/SqlParserServiceImpl.java
@@ -0,0 +1,27 @@
+package cn.bunny.service.impl;
+
+import cn.bunny.core.SqlParserCore;
+import cn.bunny.dao.entity.TableMetaData;
+import cn.bunny.dao.vo.TableInfoVo;
+import cn.bunny.service.SqlParserService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SqlParserServiceImpl implements SqlParserService {
+
+ /**
+ * 解析SQL内容
+ *
+ * @param sql Sql语句
+ * @return 表信息内容
+ */
+ @Override
+ public TableInfoVo tableInfo(String sql) {
+ TableInfoVo tableInfoVo = new TableInfoVo();
+
+ TableMetaData tableMetaData = SqlParserCore.parserTableInfo(sql);
+ BeanUtils.copyProperties(tableMetaData, tableInfoVo);
+ return tableInfoVo;
+ }
+}
diff --git a/generator-code/src/main/java/cn/bunny/service/impl/TableServiceImpl.java b/generator-code/src/main/java/cn/bunny/service/impl/TableServiceImpl.java
index db6781c..c34420f 100644
--- a/generator-code/src/main/java/cn/bunny/service/impl/TableServiceImpl.java
+++ b/generator-code/src/main/java/cn/bunny/service/impl/TableServiceImpl.java
@@ -1,7 +1,6 @@
package cn.bunny.service.impl;
import cn.bunny.core.DatabaseInfoCore;
-import cn.bunny.core.TypeConvertCore;
import cn.bunny.dao.entity.ColumnMetaData;
import cn.bunny.dao.entity.DatabaseInfoMetaData;
import cn.bunny.dao.entity.TableMetaData;
@@ -9,24 +8,14 @@ import cn.bunny.dao.vo.TableInfoVo;
import cn.bunny.service.TableService;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
-import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
@Service
public class TableServiceImpl implements TableService {
- @Resource
- private DataSource dataSource;
-
@Resource
private DatabaseInfoCore databaseInfoCore;
@@ -55,26 +44,7 @@ public class TableServiceImpl implements TableService {
@SneakyThrows
@Override
public List databaseTableList(String dbName) {
-
- // 当前数据库数据库所有的表
- List allTableInfo = new ArrayList<>();
-
- try (Connection connection = dataSource.getConnection()) {
- DatabaseMetaData metaData = connection.getMetaData();
-
- // 当前数据库中所有的表
- ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
-
- while (tables.next()) {
- // 表名称
- dbName = tables.getString("TABLE_NAME");
-
- // 设置表信息
- TableMetaData tableMetaData = databaseInfoCore.tableInfoMetaData(dbName);
- allTableInfo.add(tableMetaData);
- }
-
- }
+ List allTableInfo = databaseInfoCore.databaseTableList(dbName);
return allTableInfo.stream().map(tableMetaData -> {
TableInfoVo tableInfoVo = new TableInfoVo();
@@ -93,60 +63,7 @@ public class TableServiceImpl implements TableService {
@SneakyThrows
@Override
public List tableColumnInfo(String tableName) {
- try (Connection connection = dataSource.getConnection()) {
- DatabaseMetaData metaData = connection.getMetaData();
-
- List columns = new ArrayList<>();
-
- // 当前表的主键
- Set primaryKeyColumns = databaseInfoCore.getPrimaryKeyColumns(tableName);
-
- // 当前表的列信息
- try (ResultSet columnsRs = metaData.getColumns(null, null, tableName, null)) {
- while (columnsRs.next()) {
- ColumnMetaData column = new ColumnMetaData();
-
- // 列字段
- String columnName = columnsRs.getString("COLUMN_NAME");
-
- // 将当前表的列类型转成 Java 类型
- String javaType = TypeConvertCore.convertToJavaType(column.getJdbcType());
-
- // 设置列字段
- column.setColumnName(columnName);
-
- // 列字段转成 下划线 -> 小驼峰
- column.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
-
- // 列字段转成 下划线 -> 大驼峰名称
- column.setUppercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName(), true));
-
- // 字段类型
- column.setJdbcType(columnsRs.getString("TYPE_NAME"));
-
- // 字段类型转 Java 类型
- column.setJavaType(javaType);
-
- // 字段类型转 JavaScript 类型
- column.setJavascriptType(StringUtils.uncapitalize(javaType));
-
- // 备注信息
- column.setComment(columnsRs.getString("REMARKS"));
-
- // 确保 primaryKeyColumns 不为空
- if (!primaryKeyColumns.isEmpty()) {
- // 是否是主键
- boolean isPrimaryKey = primaryKeyColumns.contains(columnName);
- column.setIsPrimaryKey(isPrimaryKey);
- }
- columns.add(column);
- }
- }
-
- columns.get(0).setIsPrimaryKey(true);
-
- return columns;
- }
+ return databaseInfoCore.tableColumnInfo(tableName);
}
/**
@@ -157,17 +74,6 @@ public class TableServiceImpl implements TableService {
@SneakyThrows
@Override
public DatabaseInfoMetaData databaseInfoMetaData() {
- try (Connection connection = dataSource.getConnection()) {
- DatabaseMetaData metaData = connection.getMetaData();
-
- return DatabaseInfoMetaData.builder()
- .databaseProductName(metaData.getDatabaseProductName())
- .databaseProductVersion(metaData.getDatabaseProductVersion())
- .driverName(metaData.getDriverName())
- .driverVersion(metaData.getDriverVersion())
- .url(metaData.getURL())
- .username(metaData.getUserName())
- .build();
- }
+ return databaseInfoCore.databaseInfoMetaData();
}
}
diff --git a/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java b/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java
index 947f284..ddeaeea 100644
--- a/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java
+++ b/generator-code/src/main/java/cn/bunny/service/impl/VmsServiceImpl.java
@@ -2,12 +2,13 @@ package cn.bunny.service.impl;
import cn.bunny.core.DatabaseInfoCore;
import cn.bunny.core.ResourceFileCore;
+import cn.bunny.core.SqlParserCore;
+import cn.bunny.core.vms.VmsArgumentDtoBaseVmsGenerator;
import cn.bunny.dao.dto.VmsArgumentDto;
import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.entity.TableMetaData;
import cn.bunny.dao.vo.GeneratorVo;
-import cn.bunny.dao.vo.TableInfoVo;
import cn.bunny.dao.vo.VmsPathVo;
-import cn.bunny.service.TableService;
import cn.bunny.service.VmsService;
import cn.bunny.utils.VmsUtil;
import cn.hutool.crypto.digest.MD5;
@@ -17,6 +18,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -32,9 +34,6 @@ import java.util.zip.ZipOutputStream;
@Service
public class VmsServiceImpl implements VmsService {
- @Resource
- private TableService tableService;
-
@Resource
private DatabaseInfoCore databaseInfoCore;
@@ -47,14 +46,25 @@ public class VmsServiceImpl implements VmsService {
@Override
public List generator(VmsArgumentDto dto) {
String tableName = dto.getTableName();
+ String sql = dto.getSql();
+
+ // 表格属性名 和 列信息
+ TableMetaData tableMetaData;
+ List columnInfoList;
+
+ // 判断是否有 SQL 如果有SQL 优先解析并生成SQL相关内容
+ if (StringUtils.hasText(sql)) {
+ tableMetaData = SqlParserCore.parserTableInfo(sql);
+ columnInfoList = SqlParserCore.parserColumnInfo(sql);
+ } else {
+ tableMetaData = databaseInfoCore.tableInfoMetaData(tableName);
+ columnInfoList = databaseInfoCore.tableColumnInfo(tableName).stream().distinct().toList();
+ }
return dto.getPath().stream().map(path -> {
- // 表格属性名 和 列信息
- TableInfoVo tableMetaData = tableService.tableMetaData(tableName);
- List columnInfoList = tableService.tableColumnInfo(tableName).stream().distinct().toList();
-
// 生成模板
- StringWriter writer = VmsUtil.buildGeneratorCodeTemplate(dto, path, tableMetaData, columnInfoList);
+ VmsArgumentDtoBaseVmsGenerator vmsArgumentDtoBaseVmsGenerator = new VmsArgumentDtoBaseVmsGenerator(dto, path);
+ StringWriter writer = vmsArgumentDtoBaseVmsGenerator.generatorCodeTemplate(tableMetaData, columnInfoList);
// 处理 vm 文件名
path = VmsUtil.handleVmFilename(path, dto.getClassName());
diff --git a/generator-code/src/main/java/cn/bunny/utils/VmsUtil.java b/generator-code/src/main/java/cn/bunny/utils/VmsUtil.java
index d65a5ae..66b788f 100644
--- a/generator-code/src/main/java/cn/bunny/utils/VmsUtil.java
+++ b/generator-code/src/main/java/cn/bunny/utils/VmsUtil.java
@@ -1,18 +1,8 @@
package cn.bunny.utils;
import cn.bunny.core.TypeConvertCore;
-import cn.bunny.dao.dto.VmsArgumentDto;
-import cn.bunny.dao.entity.ColumnMetaData;
-import cn.bunny.dao.vo.TableInfoVo;
import com.google.common.base.CaseFormat;
-import org.apache.velocity.Template;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.Velocity;
-import java.io.StringWriter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
import java.util.Map;
public class VmsUtil {
@@ -25,66 +15,6 @@ public class VmsUtil {
"resourceMapper", "Mapper"
);
- /**
- * 生成模板
- *
- * @param dto 类名称可以自定义,格式为 xxx_xxx
- * @param path 当前路径
- * @param tableMetaData 表属性
- * @param columnInfoList 列属性数组
- * @return StringWriter
- */
- public static StringWriter buildGeneratorCodeTemplate(VmsArgumentDto dto, String path, TableInfoVo tableMetaData, List columnInfoList) {
- StringWriter writer = new StringWriter();
- String date = new SimpleDateFormat(dto.getSimpleDateFormat()).format(new Date());
- List list = columnInfoList.stream().map(ColumnMetaData::getColumnName).toList();
-
- // 添加要生成的属性
- VelocityContext context = new VelocityContext();
-
- // 类名称如果是小驼峰,需要 [手写] 为 [下划线] 之后由 [代码 -> 小驼峰/大驼峰]
- String className = dto.getClassName();
- // 去除表开头前缀
- String tablePrefixes = dto.getTablePrefixes();
-
- // vm 不能直接写 `{` 需要转换下
- context.put("leftBrace", "{");
- // 当前日期
- context.put("date", date);
- // 作者名字
- context.put("author", dto.getAuthor());
- // 每个 Controller 上的请求前缀
- context.put("requestMapping", dto.getRequestMapping());
- // 当前的表名
- context.put("tableName", tableMetaData.getTableName());
- // 表字段的注释内容
- context.put("comment", dto.getComment());
- // 设置包名称
- context.put("package", dto.getPackageName());
- // 当前表的列信息
- context.put("columnInfoList", columnInfoList);
- // 数据库sql列
- context.put("baseColumnList", String.join(",", list));
-
- // 将 表前缀 转成数组
- String replaceTableName = "";
- for (String prefix : tablePrefixes.split("[,,]")) {
- replaceTableName = className.replace(prefix, "");
- }
-
- // 将类名称转成小驼峰
- String toCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName);
- context.put("classLowercaseName", toCamelCase);
- // 将类名称转成大驼峰
- String convertToCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName, true);
- context.put("classUppercaseName", convertToCamelCase);
- // Velocity 生成模板
- Template servicePathTemplate = Velocity.getTemplate("vms/" + path, "UTF-8");
- servicePathTemplate.merge(context, writer);
-
- return writer;
- }
-
/**
* 处理 vm 文件名
*
diff --git a/generator-code/src/test/java/cn/bunny/SqlParserTest.java b/generator-code/src/test/java/cn/bunny/SqlParserTest.java
new file mode 100644
index 0000000..859ef28
--- /dev/null
+++ b/generator-code/src/test/java/cn/bunny/SqlParserTest.java
@@ -0,0 +1,105 @@
+package cn.bunny;
+
+import cn.bunny.core.TypeConvertCore;
+import cn.bunny.dao.entity.ColumnMetaData;
+import cn.bunny.dao.entity.TableMetaData;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.create.table.CreateTable;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SqlParserTest {
+
+ @Test
+ public void test() throws JSQLParserException {
+ String sql = """
+ CREATE TABLE `sys_files` (
+ `id` bigint NOT NULL COMMENT '文件的唯一标识符,自动递增',
+ `filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件的名称',
+ `filepath` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件在服务器上的存储路径',
+ `file_size` int NOT NULL COMMENT '文件的大小,以字节为单位',
+ `file_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '文件的MIME类型',
+ `download_count` int NULL DEFAULT 0 COMMENT '下载数量',
+ `create_user` bigint NOT NULL COMMENT '创建用户',
+ `update_user` bigint NULL DEFAULT NULL COMMENT '操作用户',
+ `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录文件最后修改的时间戳',
+ `is_deleted` tinyint(1) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '文件是否被删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ INDEX `idx_filename`(`filename` ASC) USING BTREE COMMENT '索引文件名',
+ INDEX `idx_filepath`(`filepath` ASC) USING BTREE COMMENT '索引文件路径',
+ INDEX `idx_file_type`(`file_type` ASC) USING BTREE COMMENT '索引文件类型',
+ INDEX `idx_update_user`(`update_user` ASC) USING BTREE COMMENT '索引创更新用户',
+ INDEX `idx_create_user`(`create_user` ASC) USING BTREE COMMENT '索引创建用户',
+ INDEX `idx_user`(`update_user` ASC, `create_user` ASC) USING BTREE COMMENT '索引创建用户和更新用户',
+ INDEX `idx_time`(`update_time` ASC, `create_time` ASC) USING BTREE COMMENT '索引创建时间和更新时间'
+ ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统文件表' ROW_FORMAT = DYNAMIC;
+ """;
+
+ TableMetaData tableInfo = new TableMetaData();
+
+ // 解析sql
+ Statement statement = CCJSqlParserUtil.parse(sql);
+ if (!(statement instanceof CreateTable createTable)) {
+ throw new IllegalArgumentException("Not a CREATE TABLE statement");
+ }
+
+ // 设置表基本信息
+ tableInfo.setTableName(createTable.getTable().getName());
+ tableInfo.setTableType("TABLE");
+ String tableOptionsStrings = String.join(" ", createTable.getTableOptionsStrings());
+
+ // 注释信息
+ Pattern pattern = Pattern.compile("COMMENT\\s*=\\s*'(.*?)'", Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(tableOptionsStrings);
+ if (matcher.find()) {
+ tableInfo.setComment(matcher.group(1));
+ }
+
+ // 解析列信息
+ List columnMetaData = createTable.getColumnDefinitions()
+ .stream().map(column -> {
+ // 列信息
+ ColumnMetaData columnInfo = new ColumnMetaData();
+
+ // 列名称
+ columnInfo.setColumnName(column.getColumnName());
+
+ // 设置 JDBC 类型
+ String dataType = column.getColDataType().getDataType();
+ columnInfo.setJdbcType(dataType);
+
+ // 设置 Java 类型
+ String javaType = TypeConvertCore.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
+ columnInfo.setJavaType(javaType);
+
+ // 设置 JavaScript 类型
+ columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType));
+
+ // 列字段转成 下划线 -> 小驼峰
+ columnInfo.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
+ // 列字段转成 下划线 -> 大驼峰名称
+ columnInfo.setUppercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName(), true));
+
+ // 解析注释
+ List columnSpecs = column.getColumnSpecs();
+ String columnSpecsString = String.join(" ", columnSpecs);
+ Matcher columnSpecsStringMatcher = Pattern.compile("COMMENT\\s*'(.*?)'", Pattern.CASE_INSENSITIVE).matcher(columnSpecsString);
+ if (columnSpecsStringMatcher.find()) {
+ columnInfo.setComment(columnSpecsStringMatcher.group(1));
+ }
+
+ return columnInfo;
+ }).toList();
+
+ System.out.println(tableInfo);
+ System.out.println("----------------------------------------------------------------------------------------");
+ System.out.println(columnMetaData);
+ }
+}