Compare commits
9 Commits
132c03a4ef
...
ec4379abe4
Author | SHA1 | Date |
---|---|---|
|
ec4379abe4 | |
|
2a829e477b | |
|
41ce317249 | |
|
9b6938b46e | |
|
2e714dc080 | |
|
74ff03182e | |
|
08c4ff9b6b | |
|
1e07831818 | |
|
5ef135de32 |
107
README.md
107
README.md
|
@ -1,43 +1,90 @@
|
|||
# 代码生成器
|
||||
# Bunny Code Generator 🚀
|
||||
|
||||
## 内置字段
|
||||
[](LICENSE)[]()[]()
|
||||
|
||||
```java
|
||||
// vm 不能直接写 `{` 需要转换下
|
||||
context.put("leftBrace", "{");
|
||||
Bunny Code Generator 是一个高效的数据库表结构到代码的生成工具,帮助开发者快速生成高质量的服务端代码,显著提升开发效率。
|
||||
|
||||
// 当前的表名
|
||||
context.put("tableName", tableMetaData.getTableName());
|
||||
## ✨ 核心特性
|
||||
|
||||
// 当前表的列信息
|
||||
context.put("columnInfoList", columnInfoList);
|
||||
- **多数据源支持** - 支持通过数据库连接或直接解析SQL语句生成代码
|
||||
- **模板引擎自由** - 基于Velocity模板引擎,支持完全自定义代码模板
|
||||
- **灵活输出方式** - 支持即时代码预览和ZIP打包下载两种输出模式
|
||||
- **智能命名转换** - 自动将表名转换为大/小驼峰命名的类名
|
||||
- **动态模板选择** - 前端可交互式选择需要生成的模板文件组合
|
||||
- **注释保留** - 自动将表字段注释带入生成的代码中
|
||||
|
||||
// 数据库sql列
|
||||
context.put("baseColumnList", String.join(",", list));
|
||||
## 🚀 快速入门
|
||||
|
||||
// 当前日期
|
||||
String date = new SimpleDateFormat(dto.getSimpleDateFormat()).format(new Date());
|
||||
context.put("date", date);
|
||||
### 前置要求
|
||||
|
||||
// 作者名字
|
||||
context.put("author", dto.getAuthor());
|
||||
- JDK 1.8 或更高版本
|
||||
- MySQL 5.7+ (或其他支持的数据库)
|
||||
- Spring Boot 2.7+
|
||||
|
||||
// 每个 Controller 上的请求前缀
|
||||
context.put("requestMapping", dto.getRequestMapping());
|
||||
### 安装步骤
|
||||
|
||||
// 表字段的注释内容
|
||||
context.put("comment", dto.getComment());
|
||||
1. 克隆仓库:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/bunny-code-generator.git
|
||||
```
|
||||
|
||||
// 设置包名称
|
||||
context.put("package", dto.getPackageName());
|
||||
2. 配置数据库连接 (`application.yml`):
|
||||
```yaml
|
||||
bunny:
|
||||
master:
|
||||
database: your_database
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/your_database
|
||||
username: your_username
|
||||
password: your_password
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
```
|
||||
|
||||
// 将类名称转成小驼峰
|
||||
String toCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName);
|
||||
context.put("classLowercaseName", toCamelCase);
|
||||
3. 启动应用:
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
// 将类名称转成大驼峰
|
||||
String convertToCamelCase = TypeConvertCore.convertToCamelCase(replaceTableName, true);
|
||||
context.put("classUppercaseName", convertToCamelCase);
|
||||
```
|
||||
4. 访问 `http://localhost:8080` 开始使用
|
||||
|
||||

|
||||
## 🛠️ 模板变量参考
|
||||
|
||||
模板中可使用以下预定义变量:
|
||||
|
||||
| 变量名 | 描述 | 示例值 |
|
||||
| -------------------- | ------------------ | ------------------ |
|
||||
| `tableName` | 原始表名 | `user_info` |
|
||||
| `classLowercaseName` | 小驼峰类名 | `userInfo` |
|
||||
| `classUppercaseName` | 大驼峰类名 | `UserInfo` |
|
||||
| `columnInfoList` | 列信息列表 | `List<ColumnInfo>` |
|
||||
| `baseColumnList` | SQL列名字符串 | `id,name,age` |
|
||||
| `package` | 包名 | `com.example.demo` |
|
||||
| `author` | 作者名 | `YourName` |
|
||||
| `date` | 当前日期 | `2023-07-20` |
|
||||
| `comment` | 表注释 | `用户信息表` |
|
||||
| `requestMapping` | Controller请求前缀 | `/api/user` |
|
||||
|
||||
## 🤝 参与贡献
|
||||
|
||||
我们欢迎各种形式的贡献!请阅读 [贡献指南](CONTRIBUTING.md) 了解如何参与项目开发。
|
||||
|
||||
1. Fork 项目仓库
|
||||
2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交您的更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 发起 Pull Request
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证 - 详情请参阅 [LICENSE](LICENSE) 文件。
|
||||
|
||||
## ☕ 支持项目
|
||||
|
||||
如果这个项目对您有帮助,可以考虑支持我们:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**Happy Coding!** 🎉
|
|
@ -1,13 +1,12 @@
|
|||
package cn.bunny.controller;
|
||||
|
||||
import cn.bunny.core.factory.ConcreteSqlParserDatabaseInfo;
|
||||
import cn.bunny.core.provider.SqlMetadataProvider;
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.domain.result.Result;
|
||||
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 lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
@ -17,23 +16,23 @@ import java.util.List;
|
|||
@Tag(name = "解析SQL", description = "解析SQL接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/sqlParser")
|
||||
@RequiredArgsConstructor
|
||||
public class SqlParserController {
|
||||
|
||||
@Resource
|
||||
private SqlParserService sqlParserService;
|
||||
private final SqlMetadataProvider sqlParserService;
|
||||
|
||||
@Operation(summary = "解析SQL成表信息", description = "解析SQL成表信息")
|
||||
@PostMapping("tableInfo")
|
||||
public Result<TableMetaData> tableInfo(String sql) {
|
||||
TableMetaData vo = sqlParserService.tableInfo(sql);
|
||||
TableMetaData vo = sqlParserService.getTableMetadata(sql);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
@Operation(summary = "解析SQL成列数据", description = "解析SQL成列数据")
|
||||
@PostMapping("columnMetaData")
|
||||
public Result<List<ColumnMetaData>> columnMetaData(String sql) {
|
||||
ConcreteSqlParserDatabaseInfo databaseInfoCore = new ConcreteSqlParserDatabaseInfo();
|
||||
List<ColumnMetaData> vo = databaseInfoCore.tableColumnInfo(sql);
|
||||
List<ColumnMetaData> vo = sqlParserService.getColumnInfoList(sql);
|
||||
return Result.success(vo);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.bunny.controller;
|
||||
|
||||
import cn.bunny.core.provider.DatabaseMetadataProvider;
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.DatabaseInfoMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
|
@ -7,7 +8,7 @@ import cn.bunny.domain.result.Result;
|
|||
import cn.bunny.service.TableService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
@ -17,11 +18,11 @@ import java.util.List;
|
|||
@Tag(name = "数据库表控制器", description = "数据库表信息接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/table")
|
||||
@RequiredArgsConstructor
|
||||
public class TableController {
|
||||
|
||||
@Resource
|
||||
private TableService tableService;
|
||||
|
||||
private final TableService tableService;
|
||||
private final DatabaseMetadataProvider databaseMetadataProvider;
|
||||
|
||||
@Operation(summary = "当前数据库信息", description = "当前连接的数据库信息")
|
||||
@GetMapping("databaseInfoMetaData")
|
||||
|
@ -33,21 +34,22 @@ public class TableController {
|
|||
@Operation(summary = "数据库所有的表", description = "获取[当前/所有]数据库表")
|
||||
@GetMapping("databaseTableList")
|
||||
public Result<List<TableMetaData>> databaseTableList(String dbName) {
|
||||
List<TableMetaData> list = tableService.databaseTableList(dbName);
|
||||
List<TableMetaData> list = databaseMetadataProvider.getTableMetadataBatch(dbName);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "表属性", description = "获取当前查询表属性")
|
||||
@GetMapping("tableMetaData")
|
||||
public Result<TableMetaData> tableMetaData(String tableName) {
|
||||
TableMetaData tableMetaData = tableService.tableMetaData(tableName);
|
||||
TableMetaData tableMetaData = databaseMetadataProvider.getTableMetadata(tableName);
|
||||
return Result.success(tableMetaData);
|
||||
}
|
||||
|
||||
@Operation(summary = "表的列属性", description = "获取当前查询表中列属性")
|
||||
@GetMapping("tableColumnInfo")
|
||||
public Result<List<ColumnMetaData>> tableColumnInfo(String tableName) {
|
||||
List<ColumnMetaData> columnInfo = tableService.tableColumnInfo(tableName);
|
||||
List<ColumnMetaData> columnInfo = databaseMetadataProvider.getColumnInfoList(tableName);
|
||||
return Result.success(columnInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,4 +42,5 @@ public class VmsController {
|
|||
public ResponseEntity<byte[]> downloadByZip(@Valid @RequestBody VmsArgumentDto dto) {
|
||||
return vmsService.downloadByZip(dto);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package cn.bunny.core.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库方言
|
||||
*/
|
||||
public interface DatabaseDialect {
|
||||
|
||||
/**
|
||||
* 提取表注释
|
||||
*
|
||||
* @param tableOptions 选项
|
||||
* @return 注释信息
|
||||
*/
|
||||
String extractTableComment(String tableOptions);
|
||||
|
||||
/**
|
||||
* 提取列注释
|
||||
*
|
||||
* @param columnSpecs 列规格
|
||||
* @return 注释信息
|
||||
*/
|
||||
String extractColumnComment(List<String> columnSpecs);
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.bunny.core.dialect;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Component
|
||||
public class MySqlDialect implements DatabaseDialect {
|
||||
|
||||
/**
|
||||
* 提取表注释
|
||||
*
|
||||
* @param tableOptions 选项
|
||||
* @return 注释信息
|
||||
*/
|
||||
@Override
|
||||
public String extractTableComment(String tableOptions) {
|
||||
Pattern pattern = Pattern.compile("COMMENT\\s*=\\s*'(.*?)'", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(tableOptions);
|
||||
return matcher.find() ? matcher.group(1) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取列注释
|
||||
*
|
||||
* @param columnSpecs 列规格
|
||||
* @return 注释信息
|
||||
*/
|
||||
@Override
|
||||
public String extractColumnComment(List<String> columnSpecs) {
|
||||
String columnSpecsString = String.join(" ", columnSpecs);
|
||||
Matcher columnSpecsStringMatcher = Pattern.compile("COMMENT\\s*'(.*?)'", Pattern.CASE_INSENSITIVE).matcher(columnSpecsString);
|
||||
return columnSpecsStringMatcher.find() ? columnSpecsStringMatcher.group(1) : null;
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package cn.bunny.core.factory;
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public abstract class AbstractDatabaseInfo {
|
||||
|
||||
public DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表的所有主键列名
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 主键列名的集合
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Set<String> findPrimaryKeyColumns(String tableName) {
|
||||
// 主键的key
|
||||
Set<String> primaryKeys = new HashSet<>();
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
// 当前表的主键
|
||||
ResultSet pkResultSet = metaData.getPrimaryKeys(null, null, tableName);
|
||||
|
||||
while (pkResultSet.next()) {
|
||||
// 列字段
|
||||
String columnName = pkResultSet.getString("COLUMN_NAME").toLowerCase();
|
||||
primaryKeys.add(columnName);
|
||||
}
|
||||
|
||||
return primaryKeys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 sql 表信息
|
||||
*
|
||||
* @param name 表名称或sql
|
||||
* @return 表西悉尼
|
||||
*/
|
||||
public abstract TableMetaData getTableMetadata(String name);
|
||||
|
||||
/**
|
||||
* 获取当前表的列属性
|
||||
*
|
||||
* @param name 表名称或sql
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
public abstract List<ColumnMetaData> tableColumnInfo(String name);
|
||||
|
||||
}
|
|
@ -1,31 +1,66 @@
|
|||
package cn.bunny.core.factory;
|
||||
package cn.bunny.core.provider;
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.DatabaseInfoMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import cn.bunny.exception.GeneratorCodeException;
|
||||
import cn.bunny.exception.MetadataNotFoundException;
|
||||
import cn.bunny.exception.MetadataProviderException;
|
||||
import cn.bunny.utils.MysqlTypeConvertUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
@Component
|
||||
public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
||||
@RequiredArgsConstructor
|
||||
public class DatabaseMetadataProvider implements IMetadataProvider {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
@Value("${bunny.master.database}")
|
||||
private String currentDatabase;
|
||||
|
||||
/**
|
||||
* 获取表的所有主键列名
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 主键列名的集合
|
||||
*/
|
||||
public Set<String> getPrimaryKeys(String tableName) {
|
||||
// 主键的key
|
||||
Set<String> primaryKeys = new HashSet<>();
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
// 当前表的主键
|
||||
ResultSet pkResultSet = metaData.getPrimaryKeys(null, null, tableName);
|
||||
|
||||
while (pkResultSet.next()) {
|
||||
// 列字段
|
||||
String columnName = pkResultSet.getString("COLUMN_NAME").toLowerCase();
|
||||
primaryKeys.add(columnName);
|
||||
}
|
||||
|
||||
return primaryKeys;
|
||||
} catch (SQLException e) {
|
||||
throw new GeneratorCodeException("Get primary key error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库所有的信息
|
||||
*
|
||||
* @return 当前连接的数据库信息属性
|
||||
*/
|
||||
@SneakyThrows
|
||||
public DatabaseInfoMetaData databaseInfoMetaData() {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
@ -39,23 +74,24 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
.username(metaData.getUserName())
|
||||
.currentDatabase(currentDatabase)
|
||||
.build();
|
||||
} catch (SQLException e) {
|
||||
throw new GeneratorCodeException("Get database info error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 sql 表信息
|
||||
*
|
||||
* @param tableName 表名称或sql
|
||||
* @param identifier 表名称或sql
|
||||
* @return 表西悉尼
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public TableMetaData getTableMetadata(String tableName) {
|
||||
public TableMetaData getTableMetadata(String identifier) {
|
||||
TableMetaData tableMetaData;
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
ResultSet tables = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
|
||||
ResultSet tables = metaData.getTables(null, null, identifier, new String[]{"TABLE"});
|
||||
|
||||
// 获取表的注释信息
|
||||
if (tables.next()) {
|
||||
|
@ -69,16 +105,18 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
String tableType = tables.getString("TABLE_TYPE");
|
||||
|
||||
tableMetaData = TableMetaData.builder()
|
||||
.tableName(tableName)
|
||||
.tableName(identifier)
|
||||
.comment(remarks)
|
||||
.tableCat(tableCat)
|
||||
.tableType(tableType)
|
||||
.build();
|
||||
} else {
|
||||
throw new RuntimeException("数据表不存在");
|
||||
throw new MetadataNotFoundException("Table not found: " + identifier);
|
||||
}
|
||||
|
||||
return tableMetaData;
|
||||
} catch (Exception e) {
|
||||
throw new GeneratorCodeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,26 +125,30 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
*
|
||||
* @return 所有表信息
|
||||
*/
|
||||
@SneakyThrows
|
||||
public List<TableMetaData> databaseTableList(String dbName) {
|
||||
// 当前数据库数据库所有的表
|
||||
public List<TableMetaData> getTableMetadataBatch(String dbName) {
|
||||
List<TableMetaData> allTableInfo = new ArrayList<>();
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
|
||||
// 当前数据库中所有的表
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = conn.getMetaData();
|
||||
ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
|
||||
|
||||
while (tables.next()) {
|
||||
// 表名称
|
||||
dbName = tables.getString("TABLE_NAME");
|
||||
String tableName = tables.getString("TABLE_NAME");
|
||||
String remarks = tables.getString("REMARKS");
|
||||
String tableCat = tables.getString("TABLE_CAT");
|
||||
String tableType = tables.getString("TABLE_TYPE");
|
||||
|
||||
// 设置表信息
|
||||
TableMetaData tableMetaData = getTableMetadata(dbName);
|
||||
TableMetaData tableMetaData = TableMetaData.builder()
|
||||
.tableName(tableName)
|
||||
.comment(remarks)
|
||||
.tableCat(tableCat)
|
||||
.tableType(tableType)
|
||||
.build();
|
||||
|
||||
allTableInfo.add(tableMetaData);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new MetadataProviderException("Failed to get batch table metadata", e);
|
||||
}
|
||||
|
||||
return allTableInfo;
|
||||
|
@ -115,20 +157,19 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
/**
|
||||
* 获取当前表的列属性
|
||||
*
|
||||
* @param tableName 表名称或sql
|
||||
* @param identifier 表名称或sql
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public List<ColumnMetaData> tableColumnInfo(String tableName) {
|
||||
public List<ColumnMetaData> getColumnInfoList(String identifier) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
Map<String, ColumnMetaData> map = new LinkedHashMap<>();
|
||||
// 当前表的主键
|
||||
Set<String> primaryKeyColumns = findPrimaryKeyColumns(tableName);
|
||||
Set<String> primaryKeyColumns = getPrimaryKeys(identifier);
|
||||
|
||||
// 当前表的列信息
|
||||
try (ResultSet columnsRs = metaData.getColumns(null, null, tableName, null)) {
|
||||
try (ResultSet columnsRs = metaData.getColumns(null, null, identifier, null)) {
|
||||
while (columnsRs.next()) {
|
||||
ColumnMetaData column = new ColumnMetaData();
|
||||
// 列字段
|
||||
|
@ -139,13 +180,13 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
// 设置列字段
|
||||
column.setColumnName(columnName);
|
||||
// 列字段转成 下划线 -> 小驼峰
|
||||
column.setLowercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
column.setLowercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
// 列字段转成 下划线 -> 大驼峰名称
|
||||
column.setUppercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName(), true));
|
||||
column.setUppercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName(), true));
|
||||
// 字段类型
|
||||
column.setJdbcType(typeName);
|
||||
// 字段类型转 Java 类型
|
||||
String javaType = TypeConvertUtil.convertToJavaType(typeName);
|
||||
String javaType = MysqlTypeConvertUtil.convertToJavaType(typeName);
|
||||
column.setJavaType(javaType);
|
||||
// 字段类型转 JavaScript 类型
|
||||
column.setJavascriptType(StringUtils.uncapitalize(javaType));
|
||||
|
@ -164,6 +205,8 @@ public class ConcreteDatabaseInfo extends AbstractDatabaseInfo {
|
|||
}
|
||||
|
||||
return new ArrayList<>(map.values());
|
||||
} catch (Exception e) {
|
||||
throw new MetadataProviderException("Failed to get table metadata for: " + identifier, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.bunny.core.provider;
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IMetadataProvider {
|
||||
|
||||
/**
|
||||
* 解析 sql 表信息
|
||||
*
|
||||
* @param identifier 表名称或sql
|
||||
* @return 表西悉尼
|
||||
*/
|
||||
TableMetaData getTableMetadata(String identifier);
|
||||
|
||||
/**
|
||||
* 获取当前表的列属性
|
||||
*
|
||||
* @param identifier 表名称或sql
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
List<ColumnMetaData> getColumnInfoList(String identifier);
|
||||
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
package cn.bunny.core.factory;
|
||||
package cn.bunny.core.provider;
|
||||
|
||||
import cn.bunny.core.dialect.DatabaseDialect;
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
import cn.bunny.exception.GeneratorCodeException;
|
||||
import cn.bunny.exception.SqlParseException;
|
||||
import cn.bunny.utils.MysqlTypeConvertUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.sf.jsqlparser.JSQLParserException;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.statement.Statement;
|
||||
|
@ -11,29 +15,33 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Component
|
||||
public class ConcreteSqlParserDatabaseInfo extends AbstractDatabaseInfo {
|
||||
@RequiredArgsConstructor
|
||||
public class SqlMetadataProvider implements IMetadataProvider {
|
||||
|
||||
private final DatabaseDialect dialect;
|
||||
|
||||
/**
|
||||
* 解析 sql 表信息
|
||||
* 先解析SQL语句,解析列字段信息
|
||||
*
|
||||
* @param sql 表名称或sql
|
||||
* @param identifier 表名称或sql
|
||||
* @return 表西悉尼
|
||||
* @see CCJSqlParserUtil 使用这个工具进行SQL的解析
|
||||
*/
|
||||
@Override
|
||||
public TableMetaData getTableMetadata(String sql) {
|
||||
public TableMetaData getTableMetadata(String identifier) {
|
||||
TableMetaData tableInfo = new TableMetaData();
|
||||
|
||||
// 解析sql
|
||||
Statement statement;
|
||||
try {
|
||||
statement = CCJSqlParserUtil.parse(sql);
|
||||
statement = CCJSqlParserUtil.parse(identifier);
|
||||
} catch (JSQLParserException e) {
|
||||
throw new RuntimeException("SQL解析失败");
|
||||
throw new GeneratorCodeException("SQL解析失败");
|
||||
}
|
||||
|
||||
if (!(statement instanceof CreateTable createTable)) {
|
||||
throw new IllegalArgumentException("缺少SQL语句");
|
||||
}
|
||||
|
@ -45,11 +53,8 @@ public class ConcreteSqlParserDatabaseInfo extends AbstractDatabaseInfo {
|
|||
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));
|
||||
}
|
||||
String comment = dialect.extractTableComment(tableOptionsStrings);
|
||||
tableInfo.setComment(comment);
|
||||
|
||||
return tableInfo;
|
||||
}
|
||||
|
@ -57,21 +62,21 @@ public class ConcreteSqlParserDatabaseInfo extends AbstractDatabaseInfo {
|
|||
/**
|
||||
* 获取当前表的列属性
|
||||
*
|
||||
* @param sql 表名称或sql
|
||||
* @param identifier 表名称或sql
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
@Override
|
||||
public List<ColumnMetaData> tableColumnInfo(String sql) {
|
||||
public List<ColumnMetaData> getColumnInfoList(String identifier) {
|
||||
// 解析sql
|
||||
Statement statement;
|
||||
try {
|
||||
statement = CCJSqlParserUtil.parse(sql);
|
||||
statement = CCJSqlParserUtil.parse(identifier);
|
||||
} catch (JSQLParserException e) {
|
||||
throw new RuntimeException("SQL解析失败");
|
||||
throw new SqlParseException("Fail parse sql", e.getCause());
|
||||
}
|
||||
|
||||
if (!(statement instanceof CreateTable createTable)) {
|
||||
throw new IllegalArgumentException("缺少SQL语句");
|
||||
throw new IllegalArgumentException("Lack of Sql Statement");
|
||||
}
|
||||
|
||||
return createTable.getColumnDefinitions()
|
||||
|
@ -87,24 +92,23 @@ public class ConcreteSqlParserDatabaseInfo extends AbstractDatabaseInfo {
|
|||
columnInfo.setJdbcType(dataType);
|
||||
|
||||
// 设置 Java 类型
|
||||
String javaType = TypeConvertUtil.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
|
||||
String javaType = MysqlTypeConvertUtil.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
|
||||
columnInfo.setJavaType(javaType);
|
||||
|
||||
// 设置 JavaScript 类型
|
||||
columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType));
|
||||
|
||||
// 列字段转成 下划线 -> 小驼峰
|
||||
columnInfo.setLowercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
columnInfo.setLowercaseName(MysqlTypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
// 列字段转成 下划线 -> 大驼峰名称
|
||||
columnInfo.setUppercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName(), true));
|
||||
columnInfo.setUppercaseName(MysqlTypeConvertUtil.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));
|
||||
}
|
||||
|
||||
// 设置列属性信息
|
||||
String comment = dialect.extractColumnComment(columnSpecs);
|
||||
columnInfo.setComment(comment);
|
||||
|
||||
return columnInfo;
|
||||
}).toList();
|
|
@ -11,7 +11,7 @@ import java.util.List;
|
|||
* 模板方法模式
|
||||
* 如果需要继承 AbstractVmsGenerator
|
||||
*/
|
||||
public abstract class AbstractVmsGeneratorTemplate {
|
||||
public abstract class AbstractTemplateGenerator {
|
||||
|
||||
/**
|
||||
* 添加生成内容
|
|
@ -2,7 +2,7 @@ package cn.bunny.core.template;
|
|||
|
||||
import cn.bunny.domain.dto.VmsArgumentDto;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
import cn.bunny.utils.MysqlTypeConvertUtil;
|
||||
import org.apache.velocity.Template;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.Velocity;
|
||||
|
@ -15,7 +15,7 @@ import java.util.Date;
|
|||
* 使用模板方法,方便扩展
|
||||
* 如果需要继承 AbstractVmsGenerator
|
||||
*/
|
||||
public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGeneratorTemplate {
|
||||
public class VmsTBaseTemplateGenerator extends AbstractTemplateGenerator {
|
||||
|
||||
private final VmsArgumentDto dto;
|
||||
private final String path;
|
||||
|
@ -26,7 +26,7 @@ public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGenerator
|
|||
* @param path 当前路径
|
||||
* @param tableMetaData 表名称
|
||||
*/
|
||||
public VmsArgumentDtoBaseVmsGeneratorTemplate(VmsArgumentDto dto, String path, TableMetaData tableMetaData) {
|
||||
public VmsTBaseTemplateGenerator(VmsArgumentDto dto, String path, TableMetaData tableMetaData) {
|
||||
this.dto = dto;
|
||||
this.path = path;
|
||||
this.tableMetaData = tableMetaData;
|
||||
|
@ -61,11 +61,11 @@ public class VmsArgumentDtoBaseVmsGeneratorTemplate extends AbstractVmsGenerator
|
|||
context.put("package", dto.getPackageName());
|
||||
|
||||
// 将类名称转成小驼峰
|
||||
String toCamelCase = TypeConvertUtil.convertToCamelCase(tableName);
|
||||
String toCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName);
|
||||
context.put("classLowercaseName", toCamelCase);
|
||||
|
||||
// 将类名称转成大驼峰
|
||||
String convertToCamelCase = TypeConvertUtil.convertToCamelCase(tableName, true);
|
||||
String convertToCamelCase = MysqlTypeConvertUtil.convertToCamelCase(tableName, true);
|
||||
context.put("classUppercaseName", convertToCamelCase);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.bunny.domain.entity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
@ -11,23 +12,25 @@ import java.util.List;
|
|||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(name = "TableMetaData", description = "表信息数据")
|
||||
public class TableMetaData {
|
||||
|
||||
/* 表名 */
|
||||
@Schema(name = "tableName", description = "表名")
|
||||
private String tableName;
|
||||
|
||||
/* 注释内容 */
|
||||
@Schema(name = "comment", description = "注释内容")
|
||||
private String comment;
|
||||
|
||||
/* 表目录 */
|
||||
@Schema(name = "tableCats", description = "表目录")
|
||||
private String tableCat;
|
||||
|
||||
/* 表类型(通常是"TABLE") */
|
||||
@Schema(name = "tableType", description = "表类型(通常是\"TABLE\")")
|
||||
private String tableType;
|
||||
|
||||
/* 类名 */
|
||||
@Schema(name = "className", description = "类名")
|
||||
private String className;
|
||||
|
||||
/* 列名称 */
|
||||
@Schema(name = "columns", description = "列名称")
|
||||
private List<ColumnMetaData> columns;
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package cn.bunny.exception;
|
||||
|
||||
public class MetadataNotFoundException extends RuntimeException {
|
||||
public MetadataNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package cn.bunny.exception;
|
||||
|
||||
public class MetadataProviderException extends RuntimeException {
|
||||
public MetadataProviderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package cn.bunny.exception;
|
||||
|
||||
public class SqlParseException extends MetadataProviderException {
|
||||
public SqlParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package cn.bunny.service;
|
||||
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
|
||||
public interface SqlParserService {
|
||||
/**
|
||||
* 解析SQL内容
|
||||
*
|
||||
* @param sql Sql语句
|
||||
* @return 表信息内容
|
||||
*/
|
||||
TableMetaData tableInfo(String sql);
|
||||
}
|
|
@ -1,40 +1,14 @@
|
|||
package cn.bunny.service;
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.DatabaseInfoMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TableService {
|
||||
|
||||
/**
|
||||
* 获取表属性
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 表属性
|
||||
*/
|
||||
TableMetaData tableMetaData(String tableName);
|
||||
|
||||
/**
|
||||
* 获取所有数据库
|
||||
*
|
||||
* @return 所有表信息
|
||||
*/
|
||||
List<TableMetaData> databaseTableList(String tableName);
|
||||
|
||||
/**
|
||||
* 获取列属性
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
List<ColumnMetaData> tableColumnInfo(String tableName);
|
||||
|
||||
/**
|
||||
* 数据库所有的信息
|
||||
*
|
||||
* @return 当前连接的数据库信息属性
|
||||
*/
|
||||
DatabaseInfoMetaData databaseInfoMetaData();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package cn.bunny.service.impl;
|
||||
|
||||
import cn.bunny.core.factory.ConcreteSqlParserDatabaseInfo;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.service.SqlParserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SqlParserServiceImpl implements SqlParserService {
|
||||
|
||||
private final ConcreteSqlParserDatabaseInfo sqlParserDatabaseInfo;
|
||||
|
||||
/**
|
||||
* 解析SQL内容
|
||||
*
|
||||
* @param sql Sql语句
|
||||
* @return 表信息内容
|
||||
*/
|
||||
@Override
|
||||
public TableMetaData tableInfo(String sql) {
|
||||
TableMetaData tableInfoVo = new TableMetaData();
|
||||
|
||||
TableMetaData tableMetaData = sqlParserDatabaseInfo.getTableMetadata(sql);
|
||||
BeanUtils.copyProperties(tableMetaData, tableInfoVo);
|
||||
return tableInfoVo;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
package cn.bunny.service.impl;
|
||||
|
||||
import cn.bunny.core.factory.ConcreteDatabaseInfo;
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.core.provider.DatabaseMetadataProvider;
|
||||
import cn.bunny.domain.entity.DatabaseInfoMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.service.TableService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -16,49 +14,16 @@ import java.util.stream.Collectors;
|
|||
@RequiredArgsConstructor
|
||||
public class TableServiceImpl implements TableService {
|
||||
|
||||
private final ConcreteDatabaseInfo databaseInfoCore;
|
||||
|
||||
/**
|
||||
* 获取表属性
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 表属性
|
||||
*/
|
||||
@Override
|
||||
public TableMetaData tableMetaData(String tableName) {
|
||||
return databaseInfoCore.getTableMetadata(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取[当前/所有]数据库表
|
||||
*
|
||||
* @return 所有表信息
|
||||
*/
|
||||
@Override
|
||||
public List<TableMetaData> databaseTableList(String dbName) {
|
||||
return databaseInfoCore.databaseTableList(dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前表的列属性
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 当前表所有的列内容
|
||||
*/
|
||||
@Override
|
||||
public List<ColumnMetaData> tableColumnInfo(String tableName) {
|
||||
return databaseInfoCore.tableColumnInfo(tableName);
|
||||
}
|
||||
private final DatabaseMetadataProvider databaseMetadataProvider;
|
||||
|
||||
/**
|
||||
* 数据库所有的信息
|
||||
*
|
||||
* @return 当前连接的数据库信息属性
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public DatabaseInfoMetaData databaseInfoMetaData() {
|
||||
List<TableMetaData> databaseTableList = databaseTableList(null);
|
||||
List<TableMetaData> databaseTableList = databaseMetadataProvider.getTableMetadataBatch(null);
|
||||
|
||||
// 将当前数据库表分组,以数据库名称为key
|
||||
List<TableMetaData> databaseList = databaseTableList.stream()
|
||||
|
@ -70,7 +35,7 @@ public class TableServiceImpl implements TableService {
|
|||
return tableInfoVo;
|
||||
}).toList();
|
||||
|
||||
DatabaseInfoMetaData databaseInfoMetaData = databaseInfoCore.databaseInfoMetaData();
|
||||
DatabaseInfoMetaData databaseInfoMetaData = databaseMetadataProvider.databaseInfoMetaData();
|
||||
databaseInfoMetaData.setDatabaseList(databaseList);
|
||||
|
||||
return databaseInfoMetaData;
|
||||
|
|
|
@ -10,7 +10,6 @@ import cn.bunny.utils.ResourceFileUtil;
|
|||
import cn.bunny.utils.VmsUtil;
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -35,24 +34,37 @@ public class VmsServiceImpl implements VmsService {
|
|||
return codeGeneratorService.generateCode(dto);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
/**
|
||||
* 获取vms文件路径
|
||||
*
|
||||
* @return vms下的文件路径
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<VmsPathVo>> vmsResourcePathList() {
|
||||
List<String> vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms");
|
||||
List<String> vmsRelativeFiles;
|
||||
Map<String, List<VmsPathVo>> listMap;
|
||||
|
||||
return vmsRelativeFiles.stream()
|
||||
.map(vmFile -> {
|
||||
String[] filepathList = vmFile.split("/");
|
||||
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
|
||||
try {
|
||||
vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms");
|
||||
listMap = vmsRelativeFiles.stream()
|
||||
.map(vmFile -> {
|
||||
String[] filepathList = vmFile.split("/");
|
||||
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
|
||||
|
||||
return VmsPathVo.builder()
|
||||
.id(VmsUtil.generateDivId())
|
||||
.name(vmFile)
|
||||
.label(filename)
|
||||
.type(filepathList[0])
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.groupingBy(VmsPathVo::getType));
|
||||
return VmsPathVo.builder()
|
||||
.id(VmsUtil.generateDivId())
|
||||
.name(vmFile)
|
||||
.label(filename)
|
||||
.type(filepathList[0])
|
||||
.build();
|
||||
})
|
||||
.collect(Collectors.groupingBy(VmsPathVo::getType));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Get error of VMS path:" + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
return listMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,7 +74,7 @@ public class VmsServiceImpl implements VmsService {
|
|||
// 下载文件名称
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
String digestHex = MD5.create().digestHex(currentTimeMillis + "");
|
||||
String generateZipFilename = "generator-" + digestHex.substring(0, 4) + ".zip";
|
||||
String generateZipFilename = "code-" + digestHex.substring(0, 6) + ".zip";
|
||||
|
||||
// 设置响应头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package cn.bunny.service.impl.vms;
|
||||
|
||||
import cn.bunny.core.factory.ConcreteDatabaseInfo;
|
||||
import cn.bunny.core.factory.ConcreteSqlParserDatabaseInfo;
|
||||
import cn.bunny.core.template.VmsArgumentDtoBaseVmsGeneratorTemplate;
|
||||
import cn.bunny.core.provider.DatabaseMetadataProvider;
|
||||
import cn.bunny.core.provider.SqlMetadataProvider;
|
||||
import cn.bunny.core.template.VmsTBaseTemplateGenerator;
|
||||
import cn.bunny.domain.dto.VmsArgumentDto;
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
|
@ -25,43 +25,54 @@ import java.util.stream.Collectors;
|
|||
@RequiredArgsConstructor
|
||||
public class VmsCodeGeneratorService {
|
||||
|
||||
private final ConcreteDatabaseInfo databaseInfoCore;
|
||||
private final ConcreteSqlParserDatabaseInfo sqlParserDatabaseInfo;
|
||||
private final DatabaseMetadataProvider databaseMetadataProvider;
|
||||
private final SqlMetadataProvider sqlMetadataProvider;
|
||||
|
||||
/**
|
||||
* 根据DTO生成代码模板
|
||||
*
|
||||
* @param dto 包含生成参数的数据传输对象
|
||||
* @return 生成的代码模板列表
|
||||
* @return 按表名分组的生成的代码模板列表
|
||||
*/
|
||||
public Map<String, List<GeneratorVo>> generateCode(VmsArgumentDto dto) {
|
||||
String sql = dto.getSql();
|
||||
// 提前获取可复用的数据
|
||||
final String sql = dto.getSql();
|
||||
final List<String> tableNames = dto.getTableNames();
|
||||
final List<String> paths = dto.getPath();
|
||||
|
||||
return dto.getTableNames().stream()
|
||||
return tableNames.parallelStream() // 使用并行流提高多表处理效率
|
||||
.map(tableName -> {
|
||||
// 获取表元数据和列信息
|
||||
TableMetaData tableMetaData = getTableMetadata(dto, tableName);
|
||||
List<ColumnMetaData> columnInfoList = getColumnInfoList(sql, tableName);
|
||||
|
||||
return dto.getPath().stream()
|
||||
.map(path -> {
|
||||
VmsArgumentDtoBaseVmsGeneratorTemplate generator = new VmsArgumentDtoBaseVmsGeneratorTemplate(dto, path, tableMetaData);
|
||||
StringWriter writer = generator.generatorCodeTemplate(tableMetaData, columnInfoList);
|
||||
String processedPath = VmsUtil.handleVmFilename(path, tableMetaData.getTableName());
|
||||
|
||||
return GeneratorVo.builder()
|
||||
.id(UUID.randomUUID().toString())
|
||||
.code(writer.toString())
|
||||
.comment(tableMetaData.getComment())
|
||||
.tableName(tableMetaData.getTableName())
|
||||
.path(processedPath)
|
||||
.build();
|
||||
})
|
||||
// 为每个路径生成模板
|
||||
return paths.stream()
|
||||
.map(path -> generateTemplate(dto, path, tableMetaData, columnInfoList))
|
||||
.toList();
|
||||
})
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.groupingBy(GeneratorVo::getTableName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成单个模板
|
||||
*/
|
||||
private GeneratorVo generateTemplate(VmsArgumentDto dto, String path,
|
||||
TableMetaData tableMetaData, List<ColumnMetaData> columnInfoList) {
|
||||
VmsTBaseTemplateGenerator generator = new VmsTBaseTemplateGenerator(dto, path, tableMetaData);
|
||||
StringWriter writer = generator.generatorCodeTemplate(tableMetaData, columnInfoList);
|
||||
String processedPath = VmsUtil.handleVmFilename(path, tableMetaData.getTableName());
|
||||
|
||||
return GeneratorVo.builder()
|
||||
.id(UUID.randomUUID().toString())
|
||||
.code(writer.toString())
|
||||
.comment(tableMetaData.getComment())
|
||||
.tableName(tableMetaData.getTableName())
|
||||
.path(processedPath)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表元数据
|
||||
*
|
||||
|
@ -71,8 +82,8 @@ public class VmsCodeGeneratorService {
|
|||
*/
|
||||
private TableMetaData getTableMetadata(VmsArgumentDto dto, String tableName) {
|
||||
return StringUtils.hasText(dto.getSql())
|
||||
? sqlParserDatabaseInfo.getTableMetadata(dto.getSql())
|
||||
: databaseInfoCore.getTableMetadata(tableName);
|
||||
? sqlMetadataProvider.getTableMetadata(dto.getSql())
|
||||
: databaseMetadataProvider.getTableMetadata(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,8 +95,8 @@ public class VmsCodeGeneratorService {
|
|||
*/
|
||||
private List<ColumnMetaData> getColumnInfoList(String sql, String tableName) {
|
||||
return StringUtils.hasText(sql)
|
||||
? sqlParserDatabaseInfo.tableColumnInfo(sql)
|
||||
: databaseInfoCore.tableColumnInfo(tableName).stream().distinct().toList();
|
||||
? sqlMetadataProvider.getColumnInfoList(sql)
|
||||
: databaseMetadataProvider.getColumnInfoList(tableName).stream().distinct().toList();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,9 @@ import java.util.zip.ZipEntry;
|
|||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 负责处理ZIP打包和下载逻辑的服务类
|
||||
* VMS代码生成系统的ZIP打包服务
|
||||
* <p>
|
||||
* 提供将生成的代码模板打包为ZIP文件并支持下载的功能
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
|
@ -23,17 +25,22 @@ public class VmsZipService {
|
|||
private final VmsCodeGeneratorService codeGeneratorService;
|
||||
|
||||
/**
|
||||
* 创建ZIP文件字节数组
|
||||
* 将生成的代码模板打包为ZIP文件
|
||||
*
|
||||
* @param dto 生成参数
|
||||
* @return ZIP文件字节数组
|
||||
* @param dto 代码生成参数DTO,包含表名、包名等配置
|
||||
* @return ZIP文件字节数组,可直接用于下载
|
||||
* @throws RuntimeException 当ZIP打包过程中发生IO异常时抛出
|
||||
*/
|
||||
public byte[] createZipFile(VmsArgumentDto dto) {
|
||||
List<GeneratorVo> generatorVoList = codeGeneratorService.generateCode(dto).values().stream().flatMap(Collection::stream).toList();
|
||||
|
||||
// 将二维代码生成结果扁平化为一维列表
|
||||
List<GeneratorVo> generatorVoList = codeGeneratorService.generateCode(dto).values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
|
||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
|
||||
|
||||
// 将所有生成的文件添加到ZIP包中
|
||||
generatorVoList.forEach(generatorVo -> addToZip(zipOutputStream, generatorVo));
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
} catch (IOException e) {
|
||||
|
@ -42,21 +49,24 @@ public class VmsZipService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 将单个生成结果添加到ZIP文件
|
||||
* 将单个代码文件添加到ZIP输出流
|
||||
*
|
||||
* @param zipOutputStream ZIP输出流
|
||||
* @param generatorVo 生成结果对象
|
||||
* @param zipOutputStream ZIP文件输出流
|
||||
* @param generatorVo 代码生成结果对象,包含文件路径和内容
|
||||
* @throws RuntimeException 当文件添加失败时抛出,包含失败文件路径信息
|
||||
*/
|
||||
private void addToZip(ZipOutputStream zipOutputStream, GeneratorVo generatorVo) {
|
||||
try {
|
||||
// 标准化文件路径:移除Velocity模板扩展名
|
||||
String path = generatorVo.getPath().replace(".vm", "");
|
||||
ZipEntry zipEntry = new ZipEntry(path);
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
zipOutputStream.putNextEntry(new ZipEntry(path));
|
||||
|
||||
// 以UTF-8编码写入文件内容,避免乱码问题
|
||||
zipOutputStream.write(generatorVo.getCode().getBytes(StandardCharsets.UTF_8));
|
||||
zipOutputStream.closeEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to add file to ZIP: " + generatorVo.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,8 +4,8 @@ import com.google.common.base.CaseFormat;
|
|||
import org.assertj.core.util.introspection.CaseFormatUtils;
|
||||
|
||||
/* 类型转换,数据库转Java类型等 */
|
||||
public class TypeConvertUtil {
|
||||
|
||||
public class MysqlTypeConvertUtil {
|
||||
|
||||
/**
|
||||
* 将数据库类型转换为Java类型
|
||||
*/
|
|
@ -28,9 +28,9 @@ public class VmsUtil {
|
|||
int splitPathsSize = splitPaths.length - 1;
|
||||
|
||||
// 大驼峰名称
|
||||
String CamelCase = TypeConvertUtil.convertToCamelCase(className, true);
|
||||
String CamelCase = MysqlTypeConvertUtil.convertToCamelCase(className, true);
|
||||
// 小驼峰名称
|
||||
String camelCase = TypeConvertUtil.convertToCamelCase(className);
|
||||
String smallCamelCase = MysqlTypeConvertUtil.convertToCamelCase(className);
|
||||
|
||||
// 当前文件名
|
||||
String filename = splitPaths[splitPathsSize];
|
||||
|
@ -52,8 +52,9 @@ public class VmsUtil {
|
|||
filename = CamelCase + typeMappingsFilename + "." + extension;
|
||||
}
|
||||
|
||||
if (filename.contains("vue") && !filename.contains("index")) {
|
||||
filename = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camelCase) + "-" + name + "." + extension;
|
||||
if ((filename.contains("vue") || filename.contains("ts") || filename.contains("js"))
|
||||
&& !filename.contains("index")) {
|
||||
filename = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, smallCamelCase) + "-" + name + "." + extension;
|
||||
}
|
||||
|
||||
splitPaths[splitPathsSize] = filename;
|
||||
|
|
|
@ -8,6 +8,7 @@ public class IndexController {
|
|||
|
||||
@GetMapping("/")
|
||||
public String index() {
|
||||
return "home";
|
||||
return "main";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ const MainCard = defineComponent({
|
|||
<p class="card-subtitle mt-2 text-muted">
|
||||
<i class="bi bi-mouse me-1"></i>
|
||||
点击 <code class="bg-primary bg-opacity-10">表名</code> 或
|
||||
<code class="bg-primary bg-opacity-10">生成</code> 进行跳转
|
||||
<code class="bg-primary bg-opacity-10">生成</code> 也可以选择
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 代码仓库链接区域:包含GitHub和Gitee仓库链接 -->
|
||||
<!-- 代码仓库链接区域:包含GitHub和Gitee仓库链接 -->
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<!-- GitHub 仓库链接 -->
|
|
@ -129,7 +129,12 @@ const MainForm = {
|
|||
</button>
|
||||
</div>
|
||||
<div class="col-md-4 btn-group">
|
||||
<button class="btn btn-primary" data-bs-title="生成全部已经选择的数据表" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-primary shadow-sm" disabled type="button"
|
||||
v-if="generatorCodeLoading">
|
||||
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
|
||||
<span role="status">生成选中表...</span>
|
||||
</button>
|
||||
<button v-else class="btn btn-primary" data-bs-title="生成全部已经选择的数据表" data-bs-toggle="tooltip"
|
||||
type="submit">
|
||||
生成选中表
|
||||
</button>
|
||||
|
@ -137,7 +142,13 @@ const MainForm = {
|
|||
@click="onClearGeneratorData">清空生成记录</button>
|
||||
</div>
|
||||
<div class="col-md-4 d-grid gap-2">
|
||||
<button class="btn btn-primary text-white" type="button" @click="onDownloadZip">下载ZIP</button>
|
||||
<button class="btn btn-primary shadow-sm" disabled type="button"
|
||||
v-if="downloadLoading">
|
||||
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
|
||||
<span role="status">下载ZIP...</span>
|
||||
</button>
|
||||
|
||||
<button v-else class="btn btn-primary text-white" type="button" @click="onDownloadZip">下载ZIP</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -151,6 +162,8 @@ const MainForm = {
|
|||
onGeneratorCode: {type: Function, required: true},
|
||||
// 清空生成记录
|
||||
onClearGeneratorData: {type: Function, required: true},
|
||||
// 生成代码加载
|
||||
generatorCodeLoading: {type: Boolean, required: true},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -169,7 +182,8 @@ const MainForm = {
|
|||
tablePrefixes: '',
|
||||
webTemplates: '',
|
||||
serverTemplates: ''
|
||||
}
|
||||
},
|
||||
downloadLoading: ref(false),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -273,6 +287,7 @@ const MainForm = {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onDownloadZip() {
|
||||
this.downloadLoading = true;
|
||||
try {
|
||||
const response = await axiosInstance({
|
||||
url: "/vms/downloadByZip",
|
||||
|
@ -314,6 +329,8 @@ const MainForm = {
|
|||
} catch (error) {
|
||||
console.error('下载失败:', error);
|
||||
}
|
||||
|
||||
this.downloadLoading = false;
|
||||
},
|
||||
|
||||
/**
|
|
@ -57,19 +57,19 @@ const MainTable = defineComponent({
|
|||
<tr :key="table.tableName" v-for="(table, index) in paginatedTableList">
|
||||
<td>
|
||||
<input class="form-check-input border-secondary" type="checkbox"
|
||||
v-model="table.checked" @change="onSelectTable($event, table)">
|
||||
v-model="table.checked" @change="onSelectTable($event.target.checked, table)">
|
||||
</td>
|
||||
<td>{{ (currentPage - 1) * itemsPerPage + index + 1 }}</td>
|
||||
<td>
|
||||
<a class="text-decoration-none" href="#" @click.prevent="onGeneratorCode(table)">
|
||||
<a class="text-decoration-none" href="#" @click.prevent="onSelectTable(!table.checked, table)">
|
||||
{{ table.tableName }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ table.comment || '无注释' }}</td>
|
||||
<td>{{ table.tableCat }}</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-outline-primary" @click="onGeneratorCode(table)">
|
||||
<i class="bi bi-gear"></i> 生成
|
||||
<button class="btn btn-sm btn-outline-primary" @click="onSelectTable(!table.checked, table)">
|
||||
<i class="bi bi-gear"></i> 选择
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -249,11 +249,11 @@ const MainTable = defineComponent({
|
|||
|
||||
/**
|
||||
* 选择单个表
|
||||
* @param {Event} event - 事件对象
|
||||
* @param {Boolean} checked - 事件对象
|
||||
* @param {Object} table - 表数据对象
|
||||
*/
|
||||
onSelectTable(event, table) {
|
||||
table.checked = event.target.checked;
|
||||
onSelectTable(checked, table) {
|
||||
table.checked = checked;
|
||||
this.updateFormSelectedTables();
|
||||
|
||||
// 更新表头全选状态
|
|
@ -65,9 +65,8 @@
|
|||
v-model:db-select="dbSelect" v-model:table-select="tableSelect"></main-card>
|
||||
|
||||
<!-- 填写生成表单 -->
|
||||
<main-form :on-clear-generator-data="onClearGeneratorData" :on-generator-code="onGeneratorCode"
|
||||
ref="mainFormRef"
|
||||
v-model:form="form"></main-form>
|
||||
<main-form :generator-code-loading="generatorCodeLoading" :on-clear-generator-data="onClearGeneratorData"
|
||||
:on-generator-code="onGeneratorCode" ref="mainFormRef" v-model:form="form"></main-form>
|
||||
|
||||
<!-- 表格显示 -->
|
||||
<main-table :db-select="dbSelect" :loading="loading" :on-generator-code="onGeneratorCode"
|
||||
|
@ -92,10 +91,10 @@
|
|||
|
||||
<!-- 引入组件 -->
|
||||
<script th:src="@{/src/components/AppHeader.js}"></script>
|
||||
<script th:src="@{/src/views/home/MainCard.js}"></script>
|
||||
<script th:src="@{/src/views/home/MainForm.js}"></script>
|
||||
<script th:src="@{/src/views/home/MainTable.js}"></script>
|
||||
<script th:src="@{/src/views/home/MainGeneratorPage.js}"></script>
|
||||
<script th:src="@{/src/views/main/MainCard.js}"></script>
|
||||
<script th:src="@{/src/views/main/MainForm.js}"></script>
|
||||
<script th:src="@{/src/views/main/MainTable.js}"></script>
|
||||
<script th:src="@{/src/views/main/MainGeneratorPage.js}"></script>
|
||||
|
||||
<script>
|
||||
const {createApp, ref} = Vue
|
||||
|
@ -113,6 +112,8 @@
|
|||
rawTableList: ref([]),
|
||||
// 是否加载
|
||||
loading: ref(false),
|
||||
// 是否正在生成
|
||||
generatorCodeLoading: ref(false),
|
||||
// 提交的表单
|
||||
form: ref({
|
||||
// 作者名称
|
||||
|
@ -164,6 +165,7 @@
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onGeneratorCode() {
|
||||
this.generatorCodeLoading = true;
|
||||
// 验证表单是否通过
|
||||
const isValidate = this.$refs.mainFormRef.validateForm();
|
||||
if (!isValidate) return;
|
||||
|
@ -172,12 +174,15 @@
|
|||
const {data, code, message} = await axiosInstance.post("/vms/generator", this.form);
|
||||
|
||||
// 判断是否请求成功
|
||||
if (code !== 200) return;
|
||||
else antd.message.success(message);
|
||||
if (code !== 200) {
|
||||
this.generatorCodeLoading = false;
|
||||
return;
|
||||
} else antd.message.success(message);
|
||||
|
||||
// 显示生成的页面
|
||||
this.generatorData = data;
|
||||
this.generatorPageFlag = true;
|
||||
this.generatorCodeLoading = false;
|
||||
|
||||
// 等待 DOM 更新,之后手动更新代码高亮
|
||||
await this.$nextTick();
|
|
@ -1,98 +0,0 @@
|
|||
package cn.bunny;
|
||||
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
public class JDBCTest {
|
||||
|
||||
private final DataSource dataSource;
|
||||
DatabaseMetaData metaData;
|
||||
|
||||
public JDBCTest(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
metaData = connection.getMetaData();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComment() throws SQLException {
|
||||
String tableName = "sys_i18n";
|
||||
TableMetaData tableMetaData;
|
||||
ResultSet tables = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
|
||||
|
||||
// 获取表的注释信息
|
||||
if (tables.next()) {
|
||||
String remarks = tables.getString("REMARKS");
|
||||
String tableCat = tables.getString("TABLE_CAT");
|
||||
|
||||
tableMetaData = TableMetaData.builder()
|
||||
.tableName(tableName)
|
||||
.comment(remarks)
|
||||
.tableCat(tableCat)
|
||||
.build();
|
||||
|
||||
System.out.println(tableMetaData);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllTableComment() throws SQLException {
|
||||
ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
|
||||
List<TableMetaData> list = new ArrayList<>();
|
||||
|
||||
while (tables.next()) {
|
||||
String tableName = tables.getString("TABLE_NAME");
|
||||
String remarks = tables.getString("REMARKS");
|
||||
String tableCat = tables.getString("TABLE_CAT");
|
||||
|
||||
TableMetaData tableMetaData = TableMetaData.builder()
|
||||
.tableName(tableName).comment(remarks)
|
||||
.tableCat(tableCat)
|
||||
.build();
|
||||
list.add(tableMetaData);
|
||||
}
|
||||
|
||||
System.out.println(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testColumnInfo() throws SQLException {
|
||||
List<ColumnMetaData> columns = new ArrayList<>();
|
||||
|
||||
try (ResultSet columnsRs = metaData.getColumns(null, null, "sys_i18n", null)) {
|
||||
while (columnsRs.next()) {
|
||||
ColumnMetaData column = new ColumnMetaData();
|
||||
column.setColumnName(columnsRs.getString("COLUMN_NAME"));
|
||||
column.setLowercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
column.setJdbcType(columnsRs.getString("TYPE_NAME"));
|
||||
column.setJavaType(TypeConvertUtil.convertToJavaType(column.getJdbcType()));
|
||||
column.setComment(columnsRs.getString("REMARKS"));
|
||||
|
||||
columns.add(column);
|
||||
System.out.println(column);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(columns);
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
package cn.bunny;
|
||||
|
||||
import cn.bunny.domain.entity.ColumnMetaData;
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
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 = TypeConvertUtil.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
|
||||
columnInfo.setJavaType(javaType);
|
||||
|
||||
// 设置 JavaScript 类型
|
||||
columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType));
|
||||
|
||||
// 列字段转成 下划线 -> 小驼峰
|
||||
columnInfo.setLowercaseName(TypeConvertUtil.convertToCamelCase(column.getColumnName()));
|
||||
// 列字段转成 下划线 -> 大驼峰名称
|
||||
columnInfo.setUppercaseName(TypeConvertUtil.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);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package cn.bunny;
|
||||
|
||||
import cn.bunny.utils.TypeConvertUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.assertj.core.util.introspection.CaseFormatUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class StringFormatTest {
|
||||
|
||||
@Test
|
||||
void test1() {
|
||||
System.out.println(CaseFormatUtils.toCamelCase("user_login"));
|
||||
System.out.println(CaseFormatUtils.toCamelCase("userLogin"));
|
||||
System.out.println(CaseFormatUtils.toCamelCase("UserLogin"));
|
||||
|
||||
System.out.println("--------------------------------");
|
||||
|
||||
System.out.println(StringUtils.lowerCase("user_login"));
|
||||
System.out.println(StringUtils.lowerCase("userLogin"));
|
||||
System.out.println(StringUtils.lowerCase("UserLogin"));
|
||||
|
||||
System.out.println("--------------------------------");
|
||||
|
||||
System.out.println(StringUtils.upperCase("user_login"));
|
||||
System.out.println(StringUtils.upperCase("userLogin"));
|
||||
System.out.println(StringUtils.upperCase("UserLogin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test2() {
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("user_login_A"));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("User_Login_A"));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("userLoginA"));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("UserLoginA"));
|
||||
|
||||
System.out.println("--------------------------------");
|
||||
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("i18n_type_A", true));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("User_Login_A", true));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("userLoginA", true));
|
||||
System.out.println(TypeConvertUtil.convertToCamelCase("UserLoginA", true));
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package cn.bunny;
|
||||
|
||||
import cn.hutool.crypto.digest.MD5;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TimeTest {
|
||||
@Test
|
||||
void timeTest() {
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
String digestHex = MD5.create().digestHex(currentTimeMillis + "");
|
||||
System.out.println(currentTimeMillis);
|
||||
System.out.println(digestHex);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package cn.bunny.service.impl;
|
||||
|
||||
import cn.bunny.domain.vo.VmsPathVo;
|
||||
import cn.bunny.utils.ResourceFileUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class VmsServiceImplTest {
|
||||
|
||||
|
||||
@Test
|
||||
void vmsResourcePathList() throws IOException, URISyntaxException {
|
||||
List<String> vmsFiles = ResourceFileUtil.getAbsoluteFiles("vms");
|
||||
System.out.println(vmsFiles);
|
||||
|
||||
System.out.println("--------------------------------------------------------------");
|
||||
|
||||
List<String> vmsRelativeFiles = ResourceFileUtil.getRelativeFiles("vms");
|
||||
System.out.println(vmsRelativeFiles);
|
||||
|
||||
System.out.println("--------------------------集合对象模式------------------------------------");
|
||||
|
||||
Map<String, List<VmsPathVo>> map = vmsRelativeFiles.stream().map(vmFile -> {
|
||||
String[] filepathList = vmFile.split("/");
|
||||
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
|
||||
|
||||
return VmsPathVo.builder().name(vmFile).label(filename).type(filepathList[0]).build();
|
||||
}).collect(Collectors.groupingBy(VmsPathVo::getType));
|
||||
|
||||
System.out.println(JSON.toJSONString(map));
|
||||
|
||||
System.out.println("----------------------------二维数组格式----------------------------------");
|
||||
List<List<VmsPathVo>> listMap = vmsRelativeFiles.stream().map(vmFile -> {
|
||||
String[] filepathList = vmFile.split("/");
|
||||
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
|
||||
|
||||
return VmsPathVo.builder().name(vmFile).label(filename).type(filepathList[0]).build();
|
||||
})
|
||||
.collect(Collectors.groupingBy(VmsPathVo::getType))
|
||||
.values().stream().toList();
|
||||
|
||||
System.out.println(JSON.toJSONString(listMap));
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package cn.bunny.utils;
|
||||
|
||||
import cn.bunny.domain.entity.TableMetaData;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
class ConcreteDatabaseInfoCoreTest {
|
||||
|
||||
String tableName = "sys_i18n";
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
|
||||
@Test
|
||||
void testTableInfoMetaData() {
|
||||
TableMetaData tableMetaData;
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
ResultSet tables = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
|
||||
|
||||
// 获取表的注释信息
|
||||
if (tables.next()) {
|
||||
String remarks = tables.getString("REMARKS");
|
||||
String tableCat = tables.getString("TABLE_CAT");
|
||||
String tableType = tables.getString("TABLE_TYPE");
|
||||
|
||||
tableMetaData = TableMetaData.builder()
|
||||
.tableName(tableName)
|
||||
.comment(remarks)
|
||||
.tableCat(tableCat)
|
||||
.tableType(tableType)
|
||||
.build();
|
||||
} else {
|
||||
throw new RuntimeException("数据表不存在");
|
||||
}
|
||||
|
||||
System.out.println(tableMetaData);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void getDbTableList() {
|
||||
String dbName = "auth_admin";
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = connection.getMetaData();
|
||||
ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
|
||||
while (tables.next()) {
|
||||
dbName = tables.getString("TABLE_NAME");
|
||||
list.add(dbName);
|
||||
}
|
||||
|
||||
System.out.println(list);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue