增加基于 SQL 导入表结构

This commit is contained in:
YunaiV
2021-02-13 11:00:03 +08:00
parent 8d6b14bc9c
commit 95757db6be
9 changed files with 310 additions and 177 deletions

View File

@ -21,6 +21,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -45,12 +46,13 @@ public class ToolCodegenController {
@Resource
private ToolCodegenService codegenService;
@ApiOperation(value = "获得数据库自带的表定义列表", notes = "会过滤掉已经导入 Codegen 的表")
@GetMapping("/db/table/list")
@ApiOperation(value = "获得数据库自带的表定义列表", notes = "会过滤掉已经导入 Codegen 的表")
@ApiImplicitParams({
@ApiImplicitParam(name = "tableName", required = true, example = "yudao", dataTypeClass = String.class),
@ApiImplicitParam(name = "tableComment", required = true, example = "芋道", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('tool:codegen:query')")
public CommonResult<List<ToolSchemaTableRespVO>> getSchemaTableList(
@RequestParam(value = "tableName", required = false) String tableName,
@RequestParam(value = "tableComment", required = false) String tableComment) {
@ -62,18 +64,18 @@ public class ToolCodegenController {
return success(ToolCodegenConvert.INSTANCE.convertList04(schemaTables));
}
@ApiOperation("获得表定义分页")
@GetMapping("/table/page")
// TODO 权限 @PreAuthorize("@ss.hasPermi('tool:gen:list')")
@ApiOperation("获得表定义分页")
@PreAuthorize("@ss.hasPermission('tool:codegen:query')")
public CommonResult<PageResult<ToolCodegenTableRespVO>> getCodeGenTablePage(@Valid ToolCodegenTablePageReqVO pageReqVO) {
PageResult<ToolCodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
return success(ToolCodegenConvert.INSTANCE.convertPage(pageResult));
}
@ApiOperation("获得表和字段的明细")
@GetMapping("/detail")
@ApiOperation("获得表和字段的明细")
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class)
// todo @PreAuthorize("@ss.hasPermi('tool:gen:query')")
@PreAuthorize("@ss.hasPermission('tool:codegen:query')")
public CommonResult<ToolCodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
ToolCodegenTableDO table = codegenService.getCodegenTablePage(tableId);
List<ToolCodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
@ -82,33 +84,55 @@ public class ToolCodegenController {
}
@ApiOperation("基于数据库的表结构,创建代码生成器的表和字段定义")
@PostMapping("/create-list")
// TODO 权限
public CommonResult<List<Long>> createCodegenList(@RequestParam("tableNames") List<String> tableNames) {
return success(codegenService.createCodeGenList(tableNames));
@ApiImplicitParam(name = "tableNames", required = true, example = "sys_user", dataTypeClass = List.class)
@PostMapping("/create-list-from-db")
@PreAuthorize("@ss.hasPermission('tool:codegen:create')")
public CommonResult<List<Long>> createCodegenListFromDB(@RequestParam("tableNames") List<String> tableNames) {
return success(codegenService.createCodegenListFromDB(tableNames));
}
@ApiOperation("基于 SQL 建表语句,创建代码生成器的表和字段定义")
@ApiImplicitParam(name = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
@PostMapping("/create-list-from-sql")
@PreAuthorize("@ss.hasPermission('tool:codegen:create')")
public CommonResult<Long> createCodegenListFromSQL(@RequestParam("sql") String sql) {
return success(codegenService.createCodegenListFromSQL(sql));
}
@ApiOperation("更新数据库的表和字段定义")
@PutMapping("/update")
// @PreAuthorize("@ss.hasPermi('tool:gen:edit')") TODO 权限
@PreAuthorize("@ss.hasPermission('tool:codegen:update')")
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody ToolCodegenUpdateReqVO updateReqVO) {
codegenService.updateCodegen(updateReqVO);
return success(true);
}
@ApiOperation("基于数据库的表结构,同步数据库的表和字段定义")
@PutMapping("/sync")
@PutMapping("/sync-from-db")
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class)
// @PreAuthorize("@ss.hasPermi('tool:gen:edit')") TODO 权限
public CommonResult<Boolean> syncCodegen(@RequestParam("tableId") Long tableId) {
codegenService.syncCodegen(tableId);
@PreAuthorize("@ss.hasPermission('tool:codegen:update')")
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
codegenService.syncCodegenFromDB(tableId);
return success(true);
}
@ApiOperation("基于 SQL 建表语句,同步数据库的表和字段定义")
@PutMapping("/sync-from-sql")
@ApiImplicitParams({
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class),
@ApiImplicitParam(name = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('tool:codegen:update')")
public CommonResult<Boolean> syncCodegenFromSQL(@RequestParam("tableId") Long tableId,
@RequestParam("sql") String sql) {
codegenService.syncCodegenFromSQL(tableId, sql);
return success(true);
}
@ApiOperation("删除数据库的表和字段定义")
@DeleteMapping("/delete")
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class)
// @PreAuthorize("@ss.hasPermi('tool:gen:remove')") TODO 权限
@PreAuthorize("@ss.hasPermission('tool:codegen:delete')")
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
codegenService.deleteCodegen(tableId);
return success(true);
@ -117,7 +141,7 @@ public class ToolCodegenController {
@ApiOperation("预览生成代码")
@GetMapping("/preview")
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class)
// @PreAuthorize("@ss.hasPermi('tool:gen:preview')") TODO 权限
@PreAuthorize("@ss.hasPermission('tool:codegen:preview')")
public CommonResult<List<ToolCodegenPreviewRespVO>> previewCodegen(@RequestParam("tableId") Long tableId) {
Map<String, String> codes = codegenService.generationCodes(tableId);
return success(ToolCodegenConvert.INSTANCE.convert(codes));
@ -126,7 +150,7 @@ public class ToolCodegenController {
@ApiOperation("下载生成代码")
@GetMapping("/download")
@ApiImplicitParam(name = "tableId", required = true, example = "表编号", dataTypeClass = Long.class)
// @PreAuthorize("@ss.hasPermi('tool:gen:code')") todo 权限
@PreAuthorize("@ss.hasPermission('tool:codegen:download')")
public void downloadCodegen(@RequestParam("tableId") Long tableId,
HttpServletResponse response) throws IOException {
// 生成代码

View File

@ -12,6 +12,10 @@ import javax.validation.constraints.NotNull;
@Data
public class ToolCodegenTableBaseVO {
@ApiModelProperty(value = "导入类型", required = true, example = "1", notes = "参见 ToolCodegenImportTypeEnum 枚举")
@NotNull(message = "导入类型不能为空")
private Integer importType;
@ApiModelProperty(value = "表名称", required = true, example = "yudao")
@NotNull(message = "表名称不能为空")
private String tableName;

View File

@ -24,6 +24,13 @@ public class ToolCodegenTableDO extends BaseDO {
*/
private Long id;
/**
* 导入类型
*
* 枚举 {@link ToolCodegenTemplateTypeEnum}
*/
private Integer importType;
// ========== 表相关字段 ==========
/**

View File

@ -12,4 +12,14 @@ public interface ToolErrorCodeConstants {
// ========== 字典类型(测试) 1003000000 ==========
ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1003000000, "测试示例不存在");
// ========== 代码生成器 1003001000 ==========
ErrorCode CODEGEN_TABLE_EXISTS = new ErrorCode(1003001000, "表定义已经存在");
ErrorCode CODEGEN_IMPORT_TABLE_NULL = new ErrorCode(1003001001, "导入的表不存在");
ErrorCode CODEGEN_IMPORT_COLUMNS_NULL = new ErrorCode(1003001002, "导入的字段不存在");
ErrorCode CODEGEN_PARSE_SQL_ERROR = new ErrorCode(1003001003, "解析 SQL 失败,请检查");
ErrorCode CODEGEN_TABLE_NOT_EXISTS = new ErrorCode(1003001004, "表定义不存在");
ErrorCode CODEGEN_COLUMN_NOT_EXISTS = new ErrorCode(1003001005, "字段义不存在");
ErrorCode CODEGEN_SYNC_COLUMNS_NULL = new ErrorCode(1003001006, "同步的字段不存在");
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1003001007, "同步失败,不存在改变");
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.dashboard.modules.tool.enums.codegen;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 代码生成的导入类型
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum ToolCodegenImportTypeEnum {
DB(1), // 从 information_schema 的 table 和 columns 表导入
SQL(2); // 基于建表 SQL 语句导入
/**
* 类型
*/
private final Integer type;
}

View File

@ -14,8 +14,6 @@ public enum ToolCodegenTemplateTypeEnum {
CRUD(1), // 单表(增删改查)
TREE(2), // 树表(增删改查)
// TODO 主子表,暂时不考虑支持。原因是较为灵活,形态较多,很难评估。
SUB(3) // 主子表(增删改查)
;
/**

View File

@ -17,6 +17,14 @@ import java.util.Map;
*/
public interface ToolCodegenService {
/**
* 基于 SQL 建表语句,创建代码生成器的表定义
*
* @param sql SQL 建表语句
* @return 创建的表定义的编号
*/
Long createCodegenListFromSQL(String sql);
/**
* 基于数据库的表结构,创建代码生成器的表定义
*
@ -31,7 +39,7 @@ public interface ToolCodegenService {
* @param tableNames 表名称数组
* @return 创建的表定义的编号数组
*/
List<Long> createCodeGenList(List<String> tableNames);
List<Long> createCodegenListFromDB(List<String> tableNames);
/**
* 更新数据库的表和字段定义
@ -45,7 +53,15 @@ public interface ToolCodegenService {
*
* @param tableId 表编号
*/
void syncCodegen(Long tableId);
void syncCodegenFromDB(Long tableId);
/**
* 基于 SQL 建表语句,同步数据库的表和字段定义
*
* @param tableId 表编号
* @param sql SQL 建表语句
*/
void syncCodegenFromSQL(Long tableId, String sql);
/**
* 删除数据库的表和字段定义

View File

@ -14,8 +14,10 @@ import cn.iocoder.dashboard.modules.tool.dal.mysql.coegen.ToolCodegenColumnMappe
import cn.iocoder.dashboard.modules.tool.dal.mysql.coegen.ToolCodegenTableMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.coegen.ToolSchemaColumnMapper;
import cn.iocoder.dashboard.modules.tool.dal.mysql.coegen.ToolSchemaTableMapper;
import cn.iocoder.dashboard.modules.tool.enums.codegen.ToolCodegenImportTypeEnum;
import cn.iocoder.dashboard.modules.tool.service.codegen.ToolCodegenService;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
import org.apache.commons.collections4.KeyValue;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -26,6 +28,9 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.dashboard.modules.tool.enums.ToolErrorCodeConstants.*;
/**
* 代码生成 Service 实现类
*
@ -51,25 +56,26 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
@Resource
private CodegenProperties codegenProperties;
@Override
@Transactional
public Long createCodegen(String tableName) {
// 从数据库中,获得数据库表结构
ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableName(tableName);
@Resource
private ToolCodegenServiceImpl self;
private Long createCodegen0(ToolCodegenImportTypeEnum importType,
ToolSchemaTableDO schemaTable, List<ToolSchemaColumnDO> schemaColumns) {
// 校验导入的表和字段非空
if (schemaTable == null) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableName);
if (CollUtil.isEmpty(schemaColumns)) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
}
// 校验是否已经存在
if (codegenTableMapper.selectByTableName(tableName) != null) {
throw new RuntimeException(""); // TODO
if (codegenTableMapper.selectByTableName(schemaTable.getTableName()) != null) {
throw exception(CODEGEN_TABLE_EXISTS);
}
// 构建 ToolCodegenTableDO 对象,插入到 DB 中
ToolCodegenTableDO table = codegenBuilder.buildTable(schemaTable);
table.setImportType(importType.getType());
codegenTableMapper.insert(table);
// 构建 ToolCodegenColumnDO 数组,插入到 DB 中
List<ToolCodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
@ -80,9 +86,34 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
return table.getId();
}
@Override
public Long createCodegenListFromSQL(String sql) {
// 从 SQL 中,获得数据库表结构
ToolSchemaTableDO schemaTable;
List<ToolSchemaColumnDO> schemaColumns;
try {
KeyValue<ToolSchemaTableDO, List<ToolSchemaColumnDO>> result = ToolCodegenSQLParser.parse(sql);
schemaTable = result.getKey();
schemaColumns = result.getValue();
} catch (Exception ex) {
throw exception(CODEGEN_PARSE_SQL_ERROR);
}
// 导入
return self.createCodegen0(ToolCodegenImportTypeEnum.SQL, schemaTable, schemaColumns);
}
@Override
public Long createCodegen(String tableName) {
// 从数据库中,获得数据库表结构
ToolSchemaTableDO schemaTable = schemaTableMapper.selectByTableName(tableName);
List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableName);
// 导入
return self.createCodegen0(ToolCodegenImportTypeEnum.DB, schemaTable, schemaColumns);
}
@Override
@Transactional
public List<Long> createCodeGenList(List<String> tableNames) {
public List<Long> createCodegenListFromDB(List<String> tableNames) {
List<Long> ids = new ArrayList<>(tableNames.size());
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
tableNames.forEach(tableName -> ids.add(createCodegen(tableName)));
@ -94,7 +125,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
public void updateCodegen(ToolCodegenUpdateReqVO updateReqVO) {
// 校验是否已经存在
if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 更新 table 表定义
@ -106,16 +137,43 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
public void syncCodegen(Long tableId) {
public void syncCodegenFromDB(Long tableId) {
// 校验是否已经存在
ToolCodegenTableDO table = codegenTableMapper.selectById(tableId);
if (table == null) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 从数据库中,获得数据库表结构
List<ToolSchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(table.getTableName());
// 执行同步
self.syncCodegen0(tableId, schemaColumns);
}
@Override
public void syncCodegenFromSQL(Long tableId, String sql) {
// 校验是否已经存在
ToolCodegenTableDO table = codegenTableMapper.selectById(tableId);
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 从 SQL 中,获得数据库表结构
List<ToolSchemaColumnDO> schemaColumns;
try {
KeyValue<ToolSchemaTableDO, List<ToolSchemaColumnDO>> result = ToolCodegenSQLParser.parse(sql);
schemaColumns = result.getValue();
} catch (Exception ex) {
throw exception(CODEGEN_PARSE_SQL_ERROR);
}
// 执行同步
self.syncCodegen0(tableId, schemaColumns);
}
private void syncCodegen0(Long tableId, List<ToolSchemaColumnDO> schemaColumns) {
// 校验导入的字段不为空
if (CollUtil.isEmpty(schemaColumns)) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_SYNC_COLUMNS_NULL);
}
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, ToolSchemaColumnDO::getColumnName);
@ -128,13 +186,13 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
Set<Long> deleteColumnIds = codegenColumns.stream().filter(column -> !schemaColumnNames.contains(column.getColumnName()))
.map(ToolCodegenColumnDO::getId).collect(Collectors.toSet());
if (CollUtil.isEmpty(schemaColumns) && CollUtil.isEmpty(deleteColumnIds)) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_SYNC_NONE_CHANGE);
}
// 插入新增的字段
List<ToolCodegenColumnDO> columns = codegenBuilder.buildColumns(schemaColumns);
columns.forEach(column -> {
column.setTableId(table.getId());
column.setTableId(tableId);
codegenColumnMapper.insert(column); // TODO 批量插入
});
// 删除不存在的字段
@ -146,7 +204,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
public void deleteCodegen(Long tableId) {
// 校验是否已经存在
if (codegenTableMapper.selectById(tableId) == null) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 删除 table 表定义
@ -180,11 +238,11 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
// 校验是否已经存在
ToolCodegenTableDO table = codegenTableMapper.selectById(tableId);
if (codegenTableMapper.selectById(tableId) == null) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
List<ToolCodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);
if (CollUtil.isEmpty(columns)) {
throw new RuntimeException(""); // TODO
throw exception(CODEGEN_COLUMN_NOT_EXISTS);
}
// 执行生成