From 4280c7684bd82120ebce209032a98b5fb7d01113 Mon Sep 17 00:00:00 2001 From: yinq Date: Fri, 11 Jul 2025 09:48:23 +0800 Subject: [PATCH] =?UTF-8?q?update=20system=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E4=BF=A1=E6=81=AF=E9=85=8D=E7=BD=AE=E3=80=81?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8C=87=E5=AE=9A=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E4=B8=8B=E8=A1=A8=E3=80=81=E5=AD=97=E6=AE=B5=E3=80=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SysDatabaseLinkController.java | 33 +++ .../service/ISysDatabaseLinkService.java | 26 +++ .../impl/SysDatabaseLinkServiceImpl.java | 207 +++++++++++++++++- 3 files changed, 261 insertions(+), 5 deletions(-) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysDatabaseLinkController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysDatabaseLinkController.java index 6329fd6..94087cd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysDatabaseLinkController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysDatabaseLinkController.java @@ -1,6 +1,8 @@ package org.dromara.system.controller; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import jakarta.servlet.http.HttpServletResponse; @@ -21,6 +23,8 @@ import org.dromara.system.domain.vo.SysDatabaseLinkVo; import org.dromara.system.domain.bo.SysDatabaseLinkBo; import org.dromara.system.service.ISysDatabaseLinkService; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.vo.TableInfoVO; +import org.dromara.system.domain.vo.ColumnInfoVO; /** * 数据库连接信息 @@ -113,4 +117,33 @@ public class SysDatabaseLinkController extends BaseController { List list = sysDatabaseLinkService.queryList(bo); return R.ok(list); } + + /** + * 查询指定数据库下所有表及表描述 + */ + @SaCheckPermission("system:databaseLink:query") + @GetMapping("/tables/{linkId}") + public R> getTables(@PathVariable Long linkId) { + return R.ok(sysDatabaseLinkService.queryTables(linkId)); + } + + /** + * 查询指定数据库下指定表的所有字段及字段描述 + */ + @SaCheckPermission("system:databaseLink:query") + @GetMapping("/columns/{linkId}/{tableName}") + public R> getColumns(@PathVariable Long linkId, @PathVariable String tableName) { + return R.ok(sysDatabaseLinkService.queryColumns(linkId, tableName)); + } + + /** + * 执行自定义SQL查询,返回数据List + */ + @SaCheckPermission("system:databaseLink:query") + @PostMapping("/querySql/{linkId}") + public R>> querySql(@PathVariable Long linkId, @RequestBody String sql) { + return R.ok(sysDatabaseLinkService.querySql(linkId, sql)); + } + + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDatabaseLinkService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDatabaseLinkService.java index dbb31c5..aeda387 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDatabaseLinkService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDatabaseLinkService.java @@ -2,12 +2,16 @@ package org.dromara.system.service; import org.dromara.system.domain.SysDatabaseLink; import org.dromara.system.domain.vo.SysDatabaseLinkVo; +import org.dromara.system.domain.vo.TableInfoVO; +import org.dromara.system.domain.vo.ColumnInfoVO; import org.dromara.system.domain.bo.SysDatabaseLinkBo; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * 数据库连接信息Service接口 @@ -66,4 +70,26 @@ public interface ISysDatabaseLinkService { * @return 是否删除成功 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 查询指定数据库下所有表及字段 + * @param linkId 数据库连接ID + * @return 表和字段信息 + */ + List queryTablesAndColumns(Long linkId); + + /** + * 查询指定数据库下所有表及表描述 + */ + List queryTables(Long linkId); + + /** + * 查询指定数据库下指定表的所有字段及字段描述 + */ + List queryColumns(Long linkId, String tableName); + + /** + * 执行自定义SQL查询,返回数据List + */ + List> querySql(Long linkId, String sql); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDatabaseLinkServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDatabaseLinkServiceImpl.java index f102c8c..b4965e6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDatabaseLinkServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDatabaseLinkServiceImpl.java @@ -7,7 +7,6 @@ import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.dromara.system.domain.bo.SysDatabaseLinkBo; @@ -15,10 +14,11 @@ import org.dromara.system.domain.vo.SysDatabaseLinkVo; import org.dromara.system.domain.SysDatabaseLink; import org.dromara.system.mapper.SysDatabaseLinkMapper; import org.dromara.system.service.ISysDatabaseLinkService; +import org.dromara.system.domain.vo.TableInfoVO; +import org.dromara.system.domain.vo.ColumnInfoVO; +import java.sql.*; +import java.util.*; -import java.util.List; -import java.util.Map; -import java.util.Collection; /** * 数据库连接信息Service业务层处理 @@ -84,7 +84,7 @@ public class SysDatabaseLinkServiceImpl implements ISysDatabaseLinkService { .eq(StringUtils.isNotBlank(bo.getJdbcUrl()), SysDatabaseLink::getJdbcUrl, bo.getJdbcUrl()) .eq(StringUtils.isNotBlank(bo.getDriverClass()), SysDatabaseLink::getDriverClass, bo.getDriverClass()) .eq(StringUtils.isNotBlank(bo.getStatus()), SysDatabaseLink::getStatus, bo.getStatus()) - .orderByDesc(SysDatabaseLink::getCreateTime); + .orderByAsc(SysDatabaseLink::getCreateTime); return lqw; } @@ -139,4 +139,201 @@ public class SysDatabaseLinkServiceImpl implements ISysDatabaseLinkService { } return baseMapper.deleteByIds(ids) > 0; } + + @Override + public List queryTablesAndColumns(Long linkId) { + SysDatabaseLink link = baseMapper.selectById(linkId); + if (link == null) { + throw new RuntimeException("数据库连接信息不存在"); + } + String url = link.getJdbcUrl(); + String username = link.getUsername(); + String password = link.getPassword(); + String driverClass = link.getDriverClass(); + List tableList = new ArrayList<>(); + try { + Class.forName(driverClass); + try (Connection conn = DriverManager.getConnection(url, username, password)) { + DatabaseMetaData metaData = conn.getMetaData(); + // 1. 查询所有表名和注释 + List tables = new ArrayList<>(); + List tableNames = new ArrayList<>(); + try (ResultSet rs = metaData.getTables(conn.getCatalog(), null, "%", new String[]{"TABLE"})) { + while (rs.next()) { + TableInfoVO table = new TableInfoVO(); + String tableName = rs.getString("TABLE_NAME"); + table.setTableName(tableName); + table.setTableComment(rs.getString("REMARKS")); + tables.add(table); + tableNames.add(tableName); + } + } + // 2. 一次性查所有字段 + Map> tableColumnMap = new HashMap<>(); + for (String tableName : tableNames) { + tableColumnMap.put(tableName, new ArrayList<>()); + } + try (ResultSet cols = metaData.getColumns(conn.getCatalog(), null, "%", "%")) { + while (cols.next()) { + String tableName = cols.getString("TABLE_NAME"); + if (!tableColumnMap.containsKey(tableName)) continue; + ColumnInfoVO col = new ColumnInfoVO(); + col.setColumnName(cols.getString("COLUMN_NAME")); + col.setDataType(cols.getString("TYPE_NAME")); + col.setColumnComment(cols.getString("REMARKS")); + tableColumnMap.get(tableName).add(col); + } + } + // 3. 组装 + for (TableInfoVO table : tables) { + table.setColumns(tableColumnMap.getOrDefault(table.getTableName(), new ArrayList<>())); + } + tableList = tables; + } + } catch (Exception e) { + throw new RuntimeException("获取表结构失败: " + e.getMessage(), e); + } + return tableList; + } + + @Override + public List queryTables(Long linkId) { + SysDatabaseLink link = baseMapper.selectById(linkId); + if (link == null) { + throw new RuntimeException("数据库连接信息不存在"); + } + String url = link.getJdbcUrl(); + String username = link.getUsername(); + String password = link.getPassword(); + String driverClass = link.getDriverClass(); + String dbType = link.getDbType() != null ? link.getDbType().toLowerCase() : driverClass.toLowerCase(); + List tableList = new ArrayList<>(); + try { + Class.forName(driverClass); + try (Connection conn = DriverManager.getConnection(url, username, password)) { + String sql = null; + if (dbType.contains("mysql")) { + sql = "SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()"; + } else if (dbType.contains("sqlserver")) { + sql = "SELECT t.name AS TABLE_NAME, ep.value AS TABLE_COMMENT FROM sys.tables t LEFT JOIN sys.extended_properties ep ON ep.major_id = t.object_id AND ep.minor_id = 0 AND ep.name = 'MS_Description' ORDER BY t.name"; + } else if (dbType.contains("oracle")) { + sql = "SELECT t.table_name AS TABLE_NAME, c.comments AS TABLE_COMMENT FROM user_tables t LEFT JOIN user_tab_comments c ON t.table_name = c.table_name"; + } else if (dbType.contains("postgresql")) { + sql = "SELECT c.relname AS TABLE_NAME, obj_description(c.oid) AS TABLE_COMMENT FROM pg_class c WHERE c.relkind = 'r' AND c.relname NOT LIKE 'pg_%' AND c.relname NOT LIKE 'sql_%'"; + } else { + // fallback: JDBC元数据 + DatabaseMetaData metaData = conn.getMetaData(); + try (ResultSet rs = metaData.getTables(conn.getCatalog(), null, "%", new String[]{"TABLE"})) { + while (rs.next()) { + TableInfoVO table = new TableInfoVO(); + table.setTableName(rs.getString("TABLE_NAME")); + table.setTableComment(rs.getString("REMARKS")); + tableList.add(table); + } + } + return tableList; + } + try (PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + TableInfoVO table = new TableInfoVO(); + table.setTableName(rs.getString("TABLE_NAME")); + table.setTableComment(rs.getString("TABLE_COMMENT")); + tableList.add(table); + } + } + } + } catch (Exception e) { + throw new RuntimeException("获取表信息失败: " + e.getMessage(), e); + } + return tableList; + } + + @Override + public List queryColumns(Long linkId, String tableName) { + SysDatabaseLink link = baseMapper.selectById(linkId); + if (link == null) { + throw new RuntimeException("数据库连接信息不存在"); + } + String url = link.getJdbcUrl(); + String username = link.getUsername(); + String password = link.getPassword(); + String driverClass = link.getDriverClass(); + String dbType = link.getDbType() != null ? link.getDbType().toLowerCase() : driverClass.toLowerCase(); + List columns = new ArrayList<>(); + try { + Class.forName(driverClass); + try (Connection conn = DriverManager.getConnection(url, username, password)) { + String sql = null; + if (dbType.contains("mysql")) { + sql = "SELECT COLUMN_NAME, COLUMN_TYPE AS DATA_TYPE, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?"; + } else if (dbType.contains("sqlserver")) { + sql = "SELECT c.name AS COLUMN_NAME, t.name AS DATA_TYPE, ep.value AS COLUMN_COMMENT FROM sys.columns c LEFT JOIN sys.types t ON c.user_type_id = t.user_type_id LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id AND ep.minor_id = c.column_id AND ep.name = 'MS_Description' WHERE c.object_id = OBJECT_ID(?)"; + } else if (dbType.contains("oracle")) { + sql = "SELECT col.column_name AS COLUMN_NAME, col.data_type AS DATA_TYPE, comm.comments AS COLUMN_COMMENT FROM user_tab_columns col LEFT JOIN user_col_comments comm ON col.table_name = comm.table_name AND col.column_name = comm.column_name WHERE col.table_name = ?"; + } else if (dbType.contains("postgresql")) { + sql = "SELECT a.attname AS COLUMN_NAME, format_type(a.atttypid, a.atttypmod) AS DATA_TYPE, col_description(a.attrelid, a.attnum) AS COLUMN_COMMENT FROM pg_attribute a WHERE a.attrelid = ?::regclass AND a.attnum > 0 AND NOT a.attisdropped"; + } else { + // fallback: JDBC元数据 + DatabaseMetaData metaData = conn.getMetaData(); + try (ResultSet rs = metaData.getColumns(conn.getCatalog(), null, tableName, "%")) { + while (rs.next()) { + ColumnInfoVO col = new ColumnInfoVO(); + col.setColumnName(rs.getString("COLUMN_NAME")); + col.setDataType(rs.getString("TYPE_NAME")); + col.setColumnComment(rs.getString("REMARKS")); + columns.add(col); + } + } + return columns; + } + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, tableName); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + ColumnInfoVO col = new ColumnInfoVO(); + col.setColumnName(rs.getString("COLUMN_NAME")); + col.setDataType(rs.getString("DATA_TYPE")); + col.setColumnComment(rs.getString("COLUMN_COMMENT")); + columns.add(col); + } + } + } + } + } catch (Exception e) { + throw new RuntimeException("获取字段信息失败: " + e.getMessage(), e); + } + return columns; + } + + @Override + public List> querySql(Long linkId, String sql) { + SysDatabaseLink link = baseMapper.selectById(linkId); + if (link == null) { + throw new RuntimeException("数据库连接信息不存在"); + } + String url = link.getJdbcUrl(); + String username = link.getUsername(); + String password = link.getPassword(); + String driverClass = link.getDriverClass(); + List> resultList = new ArrayList<>(); + try { + Class.forName(driverClass); + try (Connection conn = DriverManager.getConnection(url, username, password); + PreparedStatement ps = conn.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); + while (rs.next()) { + LinkedHashMap row = new LinkedHashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(metaData.getColumnLabel(i), rs.getObject(i)); + } + resultList.add(row); + } + } + } catch (Exception e) { + throw new RuntimeException("SQL执行失败: " + e.getMessage(), e); + } + return resultList; + } }