feat: 新增解析SQL
This commit is contained in:
parent
4f0007ee01
commit
ff2f3144dc
|
@ -64,6 +64,11 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>5.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
|
@ -74,7 +79,7 @@
|
|||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
|
|
|
@ -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<TableInfoVo> tableInfo(String sql) {
|
||||
TableInfoVo vo = sqlParserService.tableInfo(sql);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Operation(summary = "解析SQL成列数据", description = "解析SQL成列数据")
|
||||
@PostMapping("columnMetaData")
|
||||
public Result<List<ColumnMetaData>> columnMetaData(String sql) {
|
||||
List<ColumnMetaData> vo = SqlParserCore.parserColumnInfo(sql);
|
||||
return Result.success(vo);
|
||||
}
|
||||
}
|
|
@ -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<TableMetaData> databaseTableList(String dbName) {
|
||||
// 当前数据库数据库所有的表
|
||||
List<TableMetaData> 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<ColumnMetaData> tableColumnInfo(String tableName) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
List<ColumnMetaData> columns = new ArrayList<>();
|
||||
// 当前表的主键
|
||||
Set<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ColumnMetaData> 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<String> 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();
|
||||
}
|
||||
}
|
|
@ -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<ColumnMetaData> columnInfoList) {
|
||||
VelocityContext context = new VelocityContext();
|
||||
|
||||
// 添加要生成的属性
|
||||
StringWriter writer = new StringWriter();
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<String> path;
|
||||
|
||||
/* SQL 语句 */
|
||||
private String sql;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -32,4 +32,6 @@ public interface VmsService {
|
|||
* @return zip 文件
|
||||
*/
|
||||
ResponseEntity<byte[]> downloadByZip(@Valid VmsArgumentDto dto);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<TableInfoVo> databaseTableList(String dbName) {
|
||||
|
||||
// 当前数据库数据库所有的表
|
||||
List<TableMetaData> 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<TableMetaData> 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<ColumnMetaData> tableColumnInfo(String tableName) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
List<ColumnMetaData> columns = new ArrayList<>();
|
||||
|
||||
// 当前表的主键
|
||||
Set<String> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GeneratorVo> generator(VmsArgumentDto dto) {
|
||||
String tableName = dto.getTableName();
|
||||
String sql = dto.getSql();
|
||||
|
||||
// 表格属性名 和 列信息
|
||||
TableMetaData tableMetaData;
|
||||
List<ColumnMetaData> 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<ColumnMetaData> 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());
|
||||
|
|
|
@ -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<ColumnMetaData> columnInfoList) {
|
||||
StringWriter writer = new StringWriter();
|
||||
String date = new SimpleDateFormat(dto.getSimpleDateFormat()).format(new Date());
|
||||
List<String> 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 文件名
|
||||
*
|
||||
|
|
|
@ -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> 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<String> 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue