优化代码生成器实现,增加 DatabaseTableDAO 抽象,支持多 db 类型

pull/2/head
YunaiV 2022-04-28 19:27:59 +08:00
parent d04271b965
commit d79549b48a
21 changed files with 419 additions and 155 deletions

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.framework.mybatis.core.util;
import java.sql.Connection;
import java.sql.DriverManager;
/**
*
*
* @author
*/
public class DatabaseUtils {
/**
*
*
* @param url
* @param username
* @param password
* @return
*/
public static boolean isConnectionOK(String url, String username, String password) {
try (Connection ignored = DriverManager.getConnection(url, username, password)) {
return true;
} catch (Exception ex) {
return false;
}
}
}

View File

@ -0,0 +1,85 @@
package cn.iocoder.yudao.framework.mybatis.core.util;
import com.baomidou.mybatisplus.annotation.DbType;
import lombok.SneakyThrows;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* JDBC
*
* @author
*/
public class JdbcUtils {
/**
*
*
* @param url
* @param username
* @param password
* @return
*/
public static boolean isConnectionOK(String url, String username, String password) {
try (Connection ignored = DriverManager.getConnection(url, username, password)) {
return true;
} catch (Exception ex) {
return false;
}
}
/**
*
*
* @param url
* @param username
* @param password
* @return
*/
@SneakyThrows
public static Connection getConnection(String url, String username, String password) {
return DriverManager.getConnection(url, username, password);
}
/**
* SQL
*
* {@link JdbcTemplate#query(String, RowMapper)} JdbcTemplate 使 Connection
*
* @param connection
* @param sql SQL
* @param handler
* @return
*/
@SneakyThrows
public static <T> List<T> query(Connection connection, String sql, RowMapper<T> handler) {
try (PreparedStatement ps = connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
// 处理结果
List<T> result = new ArrayList<>();
int rowNum = 0;
while (rs.next()) {
result.add(handler.mapRow(rs, rowNum++));
}
return result;
}
}
/**
* URL DB
*
* @param url URL
* @return DB
*/
public static DbType getDbType(String url) {
String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null);
return DbType.getDbType(name);
}
}

View File

@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTab
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import cn.iocoder.yudao.module.infra.service.codegen.CodegenService;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
@ -58,7 +58,7 @@ public class CodegenController {
@RequestParam(value = "tableName", required = false) String tableName,
@RequestParam(value = "tableComment", required = false) String tableComment) {
// 获得数据库自带的表定义列表
List<SchemaTableDO> schemaTables = codegenService.getSchemaTableList(tableName, tableComment);
List<DatabaseTableDO> schemaTables = codegenService.getSchemaTableList(tableName, tableComment);
// 移除在 Codegen 中,已经存在的
Set<String> existsTables = CollectionUtils.convertSet(codegenService.getCodeGenTableList(), CodegenTableDO::getTableName);
schemaTables.removeIf(table -> existsTables.contains(table.getTableName()));

View File

@ -9,8 +9,8 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTa
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.SchemaTableRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@ -25,11 +25,11 @@ public interface CodegenConvert {
// ========== InformationSchemaTableDO 和 InformationSchemaColumnDO 相关 ==========
CodegenTableDO convert(SchemaTableDO bean);
CodegenTableDO convert(DatabaseTableDO bean);
List<CodegenColumnDO> convertList(List<SchemaColumnDO> list);
List<CodegenColumnDO> convertList(List<DatabaseColumnDO> list);
CodegenTableRespVO convert(SchemaColumnDO bean);
CodegenTableRespVO convert(DatabaseColumnDO bean);
// ========== CodegenTableDO 相关 ==========
@ -47,7 +47,7 @@ public interface CodegenConvert {
List<CodegenColumnDO> convertList03(List<CodegenUpdateReqVO.Column> columns);
List<SchemaTableRespVO> convertList04(List<SchemaTableDO> list);
List<SchemaTableRespVO> convertList04(List<DatabaseTableDO> list);
// ========== 其它 ==========

View File

@ -74,7 +74,6 @@ public class CodegenColumnDO extends BaseDO {
/**
* Java
*/
// @NotBlank(message = "Java属性不能为空")
private String javaField;
/**
*

View File

@ -13,6 +13,11 @@ import lombok.Data;
@Data
public class DataSourceConfigDO extends BaseDO {
/**
* - Master
*/
public static final Long ID_MASTER = 0L;
/**
*
*/

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
package cn.iocoder.yudao.module.infra.dal.dataobject.db;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@ -10,10 +10,9 @@ import lombok.Data;
*
* @author
*/
@TableName(value = "information_schema.columns", autoResultMap = true)
@Data
@Builder
public class SchemaColumnDO {
public class DatabaseColumnDO {
/**
*
@ -34,17 +33,14 @@ public class SchemaColumnDO {
/**
*
*/
@TableField("case when is_nullable = 'yes' then '1' else '0' end")
private Boolean nullable;
/**
*
*/
@TableField("case when column_key = 'PRI' then '1' else '0' end")
private Boolean primaryKey;
/**
*
*/
@TableField("case when extra = 'auto_increment' then '1' else '0' end")
private Boolean autoIncrement;
/**
*

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.codegen;
package cn.iocoder.yudao.module.infra.dal.dataobject.db;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;
@ -11,15 +10,10 @@ import java.util.Date;
*
* @author
*/
@TableName(value = "information_schema.tables", autoResultMap = true)
@Data
@Builder
public class SchemaTableDO {
public class DatabaseTableDO {
/**
*
*/
private String tableSchema;
/**
*
*/

View File

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SchemaColumnMapper extends BaseMapperX<SchemaColumnDO> {
default List<SchemaColumnDO> selectListByTableName(String tableSchema, String tableName) {
return selectList(new QueryWrapper<SchemaColumnDO>().eq("table_name", tableName)
.eq("table_schema", tableSchema)
.orderByAsc("ordinal_position"));
}
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.codegen;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SchemaTableMapper extends BaseMapperX<SchemaTableDO> {
default List<SchemaTableDO> selectList(Collection<String> tableSchemas, String tableName, String tableComment) {
return selectList(new QueryWrapperX<SchemaTableDO>().in("table_schema", tableSchemas)
.likeIfPresent("table_name", tableName)
.likeIfPresent("table_comment", tableComment));
}
default SchemaTableDO selectByTableSchemaAndTableName(String tableSchema, String tableName) {
return selectOne(new QueryWrapper<SchemaTableDO>().eq("table_schema",tableSchema)
.eq("table_name", tableName));
}
}

View File

@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.infra.dal.mysql.db;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import com.baomidou.mybatisplus.annotation.DbType;
import java.sql.Connection;
import java.util.List;
/**
* Table DAO
*
* @author
*/
public interface DatabaseTableDAO {
/**
* +
*
* @param connection
* @param tableNameLike
* @param tableCommentLike
* @return
*/
List<DatabaseTableDO> selectTableList(Connection connection, String tableNameLike, String tableCommentLike);
/**
*
*
* @param connection
* @param tableName
* @return
*/
default DatabaseTableDO selectTable(Connection connection, String tableName) {
// 考虑到对性能没有要求,直接查询列表,然后内存过滤到记录
List<DatabaseTableDO> tables = selectTableList(connection, tableName, null);
return CollUtil.findOne(tables, table -> table.getTableName().equalsIgnoreCase(tableName));
}
/**
*
*
* @param connection
* @param tableName
* @return
*/
List<DatabaseColumnDO> selectColumnList(Connection connection, String tableName);
/**
*
*
* @return
*/
DbType getType();
}

View File

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.infra.dal.mysql.db;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import com.baomidou.mybatisplus.annotation.DbType;
import org.springframework.stereotype.Repository;
import java.sql.Connection;
import java.util.List;
/**
* {@link DatabaseTableDAO} MySQL
*
* @author
*/
@Repository
public class DatabaseTableMySQLDAOImpl implements DatabaseTableDAO {
@Override
public List<DatabaseTableDO> selectTableList(Connection connection, String tableNameLike, String tableCommentLike) {
// 拼接 SQL
String sql = "SELECT table_name, table_comment, create_time" +
" FROM information_schema.TABLES" +
" WHERE table_schema = (SELECT DATABASE())";
if (StrUtil.isNotEmpty(tableNameLike)) {
sql += StrUtil.format(" AND table_name LIKE '%{}%'", tableNameLike);
}
if (StrUtil.isNotEmpty(tableCommentLike)) {
sql += StrUtil.format(" AND table_comment LIKE '%{}%'", tableCommentLike);
}
// 执行并返回结果
return JdbcUtils.query(connection, sql, (rs, rowNum) -> DatabaseTableDO.builder()
.tableName(rs.getString("table_name"))
.tableComment(rs.getString("table_comment"))
.createTime(rs.getDate("create_time"))
.build());
}
@Override
public List<DatabaseColumnDO> selectColumnList(Connection connection, String tableName) {
// 拼接 SQL
String sql = "SELECT table_name, column_name, column_type, column_comment, " +
" (CASE WHEN is_nullable = 'yes' THEN '1' ELSE '0' END) AS nullable," +
" (CASE WHEN column_key = 'PRI' THEN '1' ELSE '0' END) AS primary_key," +
" (CASE WHEN extra = 'auto_increment' THEN '1' ELSE '0' END) AS auto_increment," +
" ordinal_position" +
" FROM information_schema.COLUMNS" +
" WHERE table_schema = (SELECT DATABASE())" +
String.format(" AND table_name = '%s'", tableName);
// 执行并返回结果
return JdbcUtils.query(connection, sql, (rs, rowNum) -> DatabaseColumnDO.builder()
.tableName(rs.getString("table_name"))
.columnName(rs.getString("column_name"))
.columnType(rs.getString("column_type"))
.columnComment(rs.getString("column_comment"))
.nullable(rs.getBoolean("nullable"))
.primaryKey(rs.getBoolean("primary_key"))
.autoIncrement(rs.getBoolean("auto_increment"))
.ordinalPosition(rs.getInt("ordinal_position"))
.build());
}
@Override
public DbType getType() {
return DbType.MYSQL;
}
}

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateRe
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import java.util.List;
import java.util.Map;
@ -119,6 +119,6 @@ public interface CodegenService {
* @param tableComment
* @return
*/
List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment);
List<DatabaseTableDO> getSchemaTableList(String tableName, String tableComment);
}

View File

@ -8,17 +8,16 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTa
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.SchemaTableMapper;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenImportTypeEnum;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenSQLParser;
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import org.apache.commons.collections4.KeyValue;
import org.springframework.stereotype.Service;
@ -43,9 +42,8 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
public class CodegenServiceImpl implements CodegenService {
@Resource
private SchemaTableMapper schemaTableMapper;
@Resource
private SchemaColumnMapper schemaColumnMapper;
private DatabaseTableService databaseTableService;
@Resource
private CodegenTableMapper codegenTableMapper;
@Resource
@ -63,7 +61,7 @@ public class CodegenServiceImpl implements CodegenService {
private CodegenProperties codegenProperties;
private Long createCodegen0(Long userId, CodegenImportTypeEnum importType,
SchemaTableDO schemaTable, List<SchemaColumnDO> schemaColumns) {
DatabaseTableDO schemaTable, List<DatabaseColumnDO> schemaColumns) {
// 校验导入的表和字段非空
if (schemaTable == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
@ -90,10 +88,10 @@ public class CodegenServiceImpl implements CodegenService {
@Override
public Long createCodegenListFromSQL(Long userId, String sql) {
// 从 SQL 中,获得数据库表结构
SchemaTableDO schemaTable;
List<SchemaColumnDO> schemaColumns;
DatabaseTableDO schemaTable;
List<DatabaseColumnDO> schemaColumns;
try {
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
KeyValue<DatabaseTableDO, List<DatabaseColumnDO>> result = CodegenSQLParser.parse(sql);
schemaTable = result.getKey();
schemaColumns = result.getValue();
} catch (Exception ex) {
@ -108,8 +106,8 @@ public class CodegenServiceImpl implements CodegenService {
// 获取当前schema
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
// 从数据库中,获得数据库表结构
SchemaTableDO schemaTable = schemaTableMapper.selectByTableSchemaAndTableName(tableSchema, tableName);
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, tableName);
DatabaseTableDO schemaTable = databaseTableService.getTable(0L, tableName);
List<DatabaseColumnDO> schemaColumns = databaseTableService.getColumnList(0L, tableName);
// 导入
return this.createCodegen0(userId, CodegenImportTypeEnum.DB, schemaTable, schemaColumns);
}
@ -147,9 +145,8 @@ public class CodegenServiceImpl implements CodegenService {
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
String tableSchema = codegenProperties.getDbSchemas().iterator().next();
// 从数据库中,获得数据库表结构
List<SchemaColumnDO> schemaColumns = schemaColumnMapper.selectListByTableName(tableSchema, table.getTableName());
List<DatabaseColumnDO> schemaColumns = databaseTableService.getColumnList(0L, table.getTableName());
// 执行同步
this.syncCodegen0(tableId, schemaColumns);
@ -164,9 +161,9 @@ public class CodegenServiceImpl implements CodegenService {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 从 SQL 中,获得数据库表结构
List<SchemaColumnDO> schemaColumns;
List<DatabaseColumnDO> schemaColumns;
try {
KeyValue<SchemaTableDO, List<SchemaColumnDO>> result = CodegenSQLParser.parse(sql);
KeyValue<DatabaseTableDO, List<DatabaseColumnDO>> result = CodegenSQLParser.parse(sql);
schemaColumns = result.getValue();
} catch (Exception ex) {
throw exception(CODEGEN_PARSE_SQL_ERROR);
@ -176,12 +173,12 @@ public class CodegenServiceImpl implements CodegenService {
this.syncCodegen0(tableId, schemaColumns);
}
private void syncCodegen0(Long tableId, List<SchemaColumnDO> schemaColumns) {
private void syncCodegen0(Long tableId, List<DatabaseColumnDO> schemaColumns) {
// 校验导入的字段不为空
if (CollUtil.isEmpty(schemaColumns)) {
throw exception(CODEGEN_SYNC_COLUMNS_NULL);
}
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, SchemaColumnDO::getColumnName);
Set<String> schemaColumnNames = CollectionUtils.convertSet(schemaColumns, DatabaseColumnDO::getColumnName);
// 构建 CodegenColumnDO 数组,只同步新增的字段
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
@ -255,8 +252,8 @@ public class CodegenServiceImpl implements CodegenService {
}
@Override
public List<SchemaTableDO> getSchemaTableList(String tableName, String tableComment) {
List<SchemaTableDO> tables = schemaTableMapper.selectList(codegenProperties.getDbSchemas(), tableName, tableComment);
public List<DatabaseTableDO> getSchemaTableList(String tableName, String tableComment) {
List<DatabaseTableDO> tables = databaseTableService.getTableList(0L, tableName, tableComment);
// TODO 强制移除 Quartz 的表,未来做成可配置
tables.removeIf(table -> table.getTableName().startsWith("QRTZ_"));
tables.removeIf(table -> table.getTableName().startsWith("ACT_"));

View File

@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
@ -22,8 +22,8 @@ import static cn.hutool.core.text.CharSequenceUtil.*;
/**
* Builder
* 1. {@link SchemaTableDO} {@link CodegenTableDO}
* 2. {@link SchemaColumnDO} {@link CodegenColumnDO}
* 1. {@link DatabaseTableDO} {@link CodegenTableDO}
* 2. {@link DatabaseColumnDO} {@link CodegenColumnDO}
*/
@Component
public class CodegenBuilder {
@ -109,7 +109,7 @@ public class CodegenBuilder {
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是需要返回的
}
public CodegenTableDO buildTable(SchemaTableDO schemaTable) {
public CodegenTableDO buildTable(DatabaseTableDO schemaTable) {
CodegenTableDO table = CodegenConvert.INSTANCE.convert(schemaTable);
initTableDefault(table);
return table;
@ -133,7 +133,7 @@ public class CodegenBuilder {
table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
}
public List<CodegenColumnDO> buildColumns(Long tableId, List<SchemaColumnDO> schemaColumns) {
public List<CodegenColumnDO> buildColumns(Long tableId, List<DatabaseColumnDO> schemaColumns) {
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(schemaColumns);
for (CodegenColumnDO column : columns) {
column.setTableId(tableId);

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.SchemaTableDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
@ -21,7 +21,7 @@ import java.util.Objects;
import static com.alibaba.druid.sql.SQLUtils.normalize;
/**
* SQL SQL {@link SchemaTableDO} {@link SchemaColumnDO}
* SQL SQL {@link DatabaseTableDO} {@link DatabaseColumnDO}
* ~
*
* @author
@ -29,18 +29,18 @@ import static com.alibaba.druid.sql.SQLUtils.normalize;
public class CodegenSQLParser {
/**
* SQL {@link SchemaTableDO} {@link SchemaColumnDO}
* SQL {@link DatabaseTableDO} {@link DatabaseColumnDO}
*
* @param sql SQL
* @return
*/
public static KeyValue<SchemaTableDO, List<SchemaColumnDO>> parse(String sql) {
public static KeyValue<DatabaseTableDO, List<DatabaseColumnDO>> parse(String sql) {
// 解析 SQL 成 Statement
SQLCreateTableStatement statement = parseCreateSQL(sql);
// 解析 Table 表
SchemaTableDO table = parseTable(statement);
DatabaseTableDO table = parseTable(statement);
// 解析 Column 字段
List<SchemaColumnDO> columns = parseColumns(statement);
List<DatabaseColumnDO> columns = parseColumns(statement);
columns.forEach(column -> column.setTableName(table.getTableName()));
// 返回
return new DefaultKeyValue<>(table, columns);
@ -61,8 +61,8 @@ public class CodegenSQLParser {
return (MySqlCreateTableStatement) repository.findTable(tableName).getStatement();
}
private static SchemaTableDO parseTable(SQLCreateTableStatement statement) {
return SchemaTableDO.builder()
private static DatabaseTableDO parseTable(SQLCreateTableStatement statement) {
return DatabaseTableDO.builder()
.tableName(statement.getTableSource().getTableName(true))
.tableComment(getCommentText(statement))
.build();
@ -75,13 +75,13 @@ public class CodegenSQLParser {
return ((SQLCharExpr) statement.getComment()).getText();
}
private static List<SchemaColumnDO> parseColumns(SQLCreateTableStatement statement) {
List<SchemaColumnDO> columns = new ArrayList<>();
private static List<DatabaseColumnDO> parseColumns(SQLCreateTableStatement statement) {
List<DatabaseColumnDO> columns = new ArrayList<>();
statement.getTableElementList().forEach(element -> parseColumn(columns, element));
return columns;
}
private static void parseColumn(List<SchemaColumnDO> columns, SQLTableElement element) {
private static void parseColumn(List<DatabaseColumnDO> columns, SQLTableElement element) {
// 处理主键
if (element instanceof SQLPrimaryKey) {
parsePrimaryKey(columns, (SQLPrimaryKey) element);
@ -93,16 +93,16 @@ public class CodegenSQLParser {
}
}
private static void parsePrimaryKey(List<SchemaColumnDO> columns, SQLPrimaryKey primaryKey) {
private static void parsePrimaryKey(List<DatabaseColumnDO> columns, SQLPrimaryKey primaryKey) {
String columnName = normalize(primaryKey.getColumns().get(0).toString()); // 暂时不考虑联合主键
// 匹配 columns 主键字段,设置为 primary
columns.stream().filter(column -> column.getColumnName().equals(columnName))
.forEach(column -> column.setPrimaryKey(true));
}
private static void parseColumnDefinition(List<SchemaColumnDO> columns, SQLColumnDefinition definition) {
private static void parseColumnDefinition(List<DatabaseColumnDO> columns, SQLColumnDefinition definition) {
String text = definition.toString().toUpperCase();
columns.add(SchemaColumnDO.builder()
columns.add(DatabaseColumnDO.builder()
.columnName(normalize(definition.getColumnName()))
.columnType(definition.getDataType().toString())
.columnComment(Objects.isNull(definition.getComment()) ? ""

View File

@ -1,10 +1,9 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import org.w3c.dom.stylesheets.LinkStyle;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import javax.validation.Valid;
import java.util.List;

View File

@ -1,22 +1,21 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.hutool.db.DbUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.sql.Connection;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
@ -37,6 +36,9 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Resource
private StringEncryptor stringEncryptor;
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Override
public Long createDataSourceConfig(DataSourceConfigCreateReqVO createReqVO) {
DataSourceConfigDO dataSourceConfig = DataSourceConfigConvert.INSTANCE.convert(createReqVO);
@ -77,6 +79,11 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Override
public DataSourceConfigDO getDataSourceConfig(Long id) {
// 如果 id 为 0默认为 master 的数据源
if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {
return buildMasterDataSourceConfig();
}
// 从 DB 中读取
DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id);
dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword()));
return dataSourceConfig;
@ -84,14 +91,26 @@ public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Override
public List<DataSourceConfigDO> getDataSourceConfigList() {
return dataSourceConfigMapper.selectList();
List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();
// 补充 master 数据源
result.add(0, buildMasterDataSourceConfig());
return result;
}
private void checkConnectionOK(DataSourceConfigDO config) {
boolean success = DatabaseUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
if (!success) {
throw exception(DATA_SOURCE_CONFIG_NOT_OK);
}
}
private DataSourceConfigDO buildMasterDataSourceConfig() {
String primary = dynamicDataSourceProperties.getPrimary();
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary)
.setUrl(dataSourceProperty.getUrl())
.setUsername(dataSourceProperty.getUsername())
.setPassword(dataSourceProperty.getPassword());
}
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import java.util.List;
/**
* Service
*
* @author
*/
public interface DatabaseTableService {
/**
* +
*
* @param dataSourceConfigId
* @param tableNameLike
* @param tableCommentLike
* @return
*/
List<DatabaseTableDO> getTableList(Long dataSourceConfigId, String tableNameLike, String tableCommentLike);
/**
*
*
* @param dataSourceConfigId
* @param tableName
* @return
*/
DatabaseTableDO getTable(Long dataSourceConfigId, String tableName);
/**
*
*
* @param dataSourceConfigId
* @param tableName
* @return
*/
List<DatabaseColumnDO> getColumnList(Long dataSourceConfigId, String tableName);
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.db.DatabaseTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.db.DatabaseTableDAO;
import com.baomidou.mybatisplus.annotation.DbType;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Connection;
import java.util.List;
/**
* Service
*
* @author
*/
@Service
public class DatabaseTableServiceImpl implements DatabaseTableService {
@Resource
private DataSourceConfigService dataSourceConfigService;
@Resource
private List<DatabaseTableDAO> databaseTableDAOs;
@Override
@SneakyThrows
public List<DatabaseTableDO> getTableList(Long dataSourceConfigId, String tableNameLike, String tableCommentLike) {
try (Connection connection = getConnection(dataSourceConfigId)) {
return getDatabaseTableDAO(dataSourceConfigId).selectTableList(connection, tableNameLike, tableCommentLike);
}
}
@Override
@SneakyThrows
public DatabaseTableDO getTable(Long dataSourceConfigId, String tableName) {
try (Connection connection = getConnection(dataSourceConfigId)) {
return getDatabaseTableDAO(dataSourceConfigId).selectTable(connection, tableName);
}
}
@Override
@SneakyThrows
public List<DatabaseColumnDO> getColumnList(Long dataSourceConfigId, String tableName) {
try (Connection connection = getConnection(dataSourceConfigId)) {
return getDatabaseTableDAO(dataSourceConfigId).selectColumnList(connection, tableName);
}
}
private Connection getConnection(Long dataSourceConfigId) {
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);
Assert.notNull(config, "数据源({}) 不存在!", dataSourceConfigId);
return JdbcUtils.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
}
private DatabaseTableDAO getDatabaseTableDAO(Long dataSourceConfigId) {
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);
Assert.notNull(config, "数据源({}) 不存在!", dataSourceConfigId);
// 获得 dbType
DbType dbType = JdbcUtils.getDbType(config.getUrl());
Assert.notNull(config, "数据源类型({}) 不存在!", config.getUrl());
// 获得 DatabaseTableDAO
DatabaseTableDAO dao = CollUtil.findOne(databaseTableDAOs, databaseTableDAO -> databaseTableDAO.getType().equals(dbType));
Assert.notNull(dao, "DAO({}) 查找不到实现!", dbType);
return dao;
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.infra.service.db;
import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
@ -41,12 +41,12 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
@Test
public void testCreateDataSourceConfig_success() {
try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// 准备参数
DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class);
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用
@ -62,7 +62,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
@Test
public void testUpdateDataSourceConfig_success() {
try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
try (MockedStatic<JdbcUtils> databaseUtilsMock = mockStatic(JdbcUtils.class)) {
// mock 数据
DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
@ -72,7 +72,7 @@ public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
});
// mock 方法
when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
databaseUtilsMock.when(() -> JdbcUtils.isConnectionOK(eq(reqVO.getUrl()),
eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
// 调用