feat(database): 实现表设计器和数据编辑功能

- 添加表设计器模态框,支持创建和修改表结构
- 实现列、索引、约束的动态添加和编辑功能- 增加数据表行数据的增删改查操作界面
- 添加工具面板的折叠展开功能和快速创建表按钮- 实现表搜索功能,支持按名称过滤表列表
- 更新Java后端模拟数据以支持新的表结构操作- 添加MySQL连接配置的字符集和编码设置
- 增加表设计器的表单控件和响应式布局样式
- 实列属性的完整现表设计器中配置选项
- 添加保存表结构时的数据收集和验证逻辑
This commit is contained in:
tzdwindows 7
2025-10-07 15:35:33 +08:00
parent 8f40542ab0
commit 9eede23a94
3 changed files with 1717 additions and 92 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
package com.axis.innovators.box.browser;
import com.axis.innovators.box.AxisInnovatorsBox;
import com.axis.innovators.box.browser.util.CodeExecutor;
import com.axis.innovators.box.browser.util.DatabaseConnectionManager;
import com.axis.innovators.box.tools.FolderCreator;
@@ -23,8 +24,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.*;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
@@ -531,24 +531,39 @@ public class MainApplication {
}
java.nio.file.Files.write(outPath, arr.toString(2).getBytes(java.nio.charset.StandardCharsets.UTF_8));
} else {
// CSV
try (java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outPath, java.nio.charset.StandardCharsets.UTF_8)) {
// header
try (java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(
outPath,
java.nio.charset.StandardCharsets.UTF_8
)) {
// 写入 UTF-8 BOM
writer.write('\uFEFF');
for (int i = 1; i <= cols; i++) {
if (i > 1) writer.write(",");
writer.write("\"" + md.getColumnLabel(i).replace("\"","\"\"") + "\"");
writer.write("\"" + md.getColumnLabel(i).replace("\"", "\"\"") + "\"");
}
writer.write("\n");
while (rs.next()) {
for (int i = 1; i <= cols; i++) {
if (i > 1) writer.write(",");
Object val = rs.getObject(i);
String cell = val == null ? "" : String.valueOf(val);
writer.write("\"" + cell.replace("\"","\"\"") + "\"");
writer.write("\"" + cell.replace("\"", "\"\"") + "\"");
}
writer.write("\n");
}
}
}
try {
String pathStr = outPath.toAbsolutePath().toString();
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
new ProcessBuilder("explorer.exe", "/select," + pathStr).start();
}
} catch (Exception ignore) {
}
JSONObject resp = new JSONObject();
@@ -811,7 +826,476 @@ public class MainApplication {
}
break;
}
case "insertRow": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
JSONObject rowData = requestJson.optJSONObject("rowData");
if (connectionId.isEmpty() || tableName.isEmpty() || rowData == null) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
Connection conn = DatabaseConnectionManager.getConnection(connectionId);
if (conn == null || conn.isClosed()) {
callback.failure(500, new JSONObject().put("status","error").put("message","连接不存在或已关闭").toString());
break;
}
try {
// 构建INSERT语句
StringBuilder columns = new StringBuilder();
StringBuilder placeholders = new StringBuilder();
java.util.List<Object> values = new java.util.ArrayList<>();
java.util.Iterator<String> keys = rowData.keys();
while (keys.hasNext()) {
String key = keys.next();
if (columns.length() > 0) {
columns.append(", ");
placeholders.append(", ");
}
columns.append(key);
placeholders.append("?");
values.add(rowData.get(key));
}
String sql = "INSERT INTO " + tableName + " (" + columns.toString() + ") VALUES (" + placeholders.toString() + ")";
try (java.sql.PreparedStatement pstmt = conn.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS)) {
for (int i = 0; i < values.size(); i++) {
pstmt.setObject(i + 1, values.get(i));
}
int affectedRows = pstmt.executeUpdate();
// 获取自增ID如果有
int newId = -1;
try (java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
newId = generatedKeys.getInt(1);
}
}
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("affectedRows", affectedRows);
if (newId != -1) {
resp.put("newId", newId);
}
resp.put("message", "插入成功");
callback.success(resp.toString());
}
} catch (SQLException ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "插入失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
case "updateRow": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
JSONObject originalData = requestJson.optJSONObject("originalData");
JSONObject updatedData = requestJson.optJSONObject("updatedData");
if (connectionId.isEmpty() || tableName.isEmpty() || originalData == null || updatedData == null) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
Connection conn = DatabaseConnectionManager.getConnection(connectionId);
if (conn == null || conn.isClosed()) {
callback.failure(500, new JSONObject().put("status","error").put("message","连接不存在或已关闭").toString());
break;
}
try {
// 构建UPDATE语句
StringBuilder setClause = new StringBuilder();
StringBuilder whereClause = new StringBuilder();
java.util.List<Object> values = new java.util.ArrayList<>();
// SET部分
java.util.Iterator<String> updateKeys = updatedData.keys();
while (updateKeys.hasNext()) {
String key = updateKeys.next();
if (setClause.length() > 0) {
setClause.append(", ");
}
setClause.append(key).append(" = ?");
values.add(updatedData.get(key));
}
// WHERE部分使用原始数据识别要更新的行
java.util.Iterator<String> originalKeys = originalData.keys();
while (originalKeys.hasNext()) {
String key = originalKeys.next();
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append(key).append(" = ?");
values.add(originalData.get(key));
}
String sql = "UPDATE " + tableName + " SET " + setClause.toString() + " WHERE " + whereClause.toString();
try (java.sql.PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < values.size(); i++) {
pstmt.setObject(i + 1, values.get(i));
}
int affectedRows = pstmt.executeUpdate();
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("affectedRows", affectedRows);
resp.put("message", "更新成功");
callback.success(resp.toString());
}
} catch (SQLException ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "更新失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
case "deleteRow": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
JSONObject rowData = requestJson.optJSONObject("rowData");
if (connectionId.isEmpty() || tableName.isEmpty() || rowData == null) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
Connection conn = DatabaseConnectionManager.getConnection(connectionId);
if (conn == null || conn.isClosed()) {
callback.failure(500, new JSONObject().put("status","error").put("message","连接不存在或已关闭").toString());
break;
}
try {
// 构建DELETE语句
StringBuilder whereClause = new StringBuilder();
java.util.List<Object> values = new java.util.ArrayList<>();
java.util.Iterator<String> keys = rowData.keys();
while (keys.hasNext()) {
String key = keys.next();
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append(key).append(" = ?");
values.add(rowData.get(key));
}
String sql = "DELETE FROM " + tableName + " WHERE " + whereClause.toString();
try (java.sql.PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < values.size(); i++) {
pstmt.setObject(i + 1, values.get(i));
}
int affectedRows = pstmt.executeUpdate();
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("affectedRows", affectedRows);
resp.put("message", "删除成功");
callback.success(resp.toString());
}
} catch (SQLException ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "删除失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
case "createTable": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
JSONArray columns = requestJson.optJSONArray("columns");
JSONObject tableOptions = requestJson.optJSONObject("tableOptions");
JSONObject fileSettings = requestJson.optJSONObject("fileSettings");
if (connectionId.isEmpty() || tableName.isEmpty() || columns == null || columns.length() == 0) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
Connection conn = DatabaseConnectionManager.getConnection(connectionId);
if (conn == null || conn.isClosed()) {
callback.failure(500, new JSONObject().put("status","error").put("message","连接不存在或已关闭").toString());
break;
}
try {
// 构建CREATE TABLE语句
StringBuilder sql = new StringBuilder("CREATE TABLE ");
sql.append(tableName).append(" (");
// 添加列定义
for (int i = 0; i < columns.length(); i++) {
JSONObject column = columns.getJSONObject(i);
String colName = column.optString("name", "");
String colType = column.optString("type", "VARCHAR(255)");
String colAttributes = column.optString("attributes", "");
if (colName.isEmpty()) {
callback.failure(400, new JSONObject().put("status","error").put("message","列名不能为空").toString());
return true;
}
if (i > 0) sql.append(", ");
sql.append(colName).append(" ").append(colType);
if (!colAttributes.isEmpty()) {
sql.append(" ").append(colAttributes);
}
}
sql.append(")");
// 添加表选项
if (tableOptions != null) {
String engine = tableOptions.optString("engine", "InnoDB");
String charset = tableOptions.optString("charset", "utf8mb4");
String collation = tableOptions.optString("collation", "utf8mb4_unicode_ci");
String comment = tableOptions.optString("comment", "");
sql.append(" ENGINE=").append(engine);
sql.append(" DEFAULT CHARSET=").append(charset);
sql.append(" COLLATE=").append(collation);
if (!comment.isEmpty()) {
sql.append(" COMMENT='").append(comment.replace("'", "''")).append("'");
}
}
try (Statement stmt = conn.createStatement()) {
stmt.executeUpdate(sql.toString());
// 保存文件设置(如果提供了)
if (fileSettings != null) {
//saveFileSettings(connectionId, tableName, fileSettings);
}
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("message", "表创建成功");
callback.success(resp.toString());
}
} catch (SQLException ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "创建表失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
case "alterTable": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
String newTableName = requestJson.optString("newTableName", "");
JSONArray columns = requestJson.optJSONArray("columns");
JSONObject tableOptions = requestJson.optJSONObject("tableOptions");
JSONObject fileSettings = requestJson.optJSONObject("fileSettings");
if (connectionId.isEmpty() || tableName.isEmpty() || columns == null || columns.length() == 0) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
Connection conn = DatabaseConnectionManager.getConnection(connectionId);
if (conn == null || conn.isClosed()) {
callback.failure(500, new JSONObject().put("status","error").put("message","连接不存在或已关闭").toString());
break;
}
try {
// 这里简化处理在实际应用中需要更复杂的ALTER TABLE逻辑
// 包括检测哪些列需要添加、修改或删除
// 首先获取当前表结构
DatabaseMetaData meta = conn.getMetaData();
java.util.List<String> existingColumns = new java.util.ArrayList<>();
try (ResultSet rs = meta.getColumns(conn.getCatalog(), null, tableName, null)) {
while (rs.next()) {
existingColumns.add(rs.getString("COLUMN_NAME"));
}
}
// 构建ALTER TABLE语句简化版
// 注意实际应用中需要更复杂的逻辑来处理不同的ALTER操作
StringBuilder sql = new StringBuilder();
// 重命名表(如果需要)
if (!newTableName.isEmpty() && !newTableName.equals(tableName)) {
sql.append("ALTER TABLE ").append(tableName).append(" RENAME TO ").append(newTableName).append("; ");
tableName = newTableName; // 更新表名用于后续操作
}
// 这里简化处理:实际应用中需要更复杂的列变更逻辑
// 可以添加新列,但不能删除或修改现有列(简化版)
for (int i = 0; i < columns.length(); i++) {
JSONObject column = columns.getJSONObject(i);
String colName = column.optString("name", "");
String colType = column.optString("type", "VARCHAR(255)");
String colAttributes = column.optString("attributes", "");
if (colName.isEmpty()) continue;
if (!existingColumns.contains(colName)) {
// 添加新列
if (sql.length() > 0 && !sql.toString().endsWith("; ")) {
sql.append("; ");
}
sql.append("ALTER TABLE ").append(tableName).append(" ADD COLUMN ")
.append(colName).append(" ").append(colType);
if (!colAttributes.isEmpty()) {
sql.append(" ").append(colAttributes);
}
}
// 注意:在实际应用中,还需要处理修改和删除列的情况
}
// 执行ALTER语句
if (sql.length() > 0) {
try (Statement stmt = conn.createStatement()) {
// 处理多条SQL语句
String[] sqlStatements = sql.toString().split(";");
for (String sqlStmt : sqlStatements) {
if (!sqlStmt.trim().isEmpty()) {
stmt.executeUpdate(sqlStmt.trim());
}
}
}
}
// 更新文件设置(如果提供了)
if (fileSettings != null) {
//saveFileSettings(connectionId, tableName, fileSettings);
}
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("message", "表结构修改成功");
callback.success(resp.toString());
} catch (SQLException ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "修改表结构失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
/*
case "uploadFile": {
String connectionId = requestJson.optString("connectionId", "");
String tableName = requestJson.optString("tableName", "");
String columnName = requestJson.optString("columnName", "");
String fileName = requestJson.optString("fileName", "");
long fileSize = requestJson.optLong("fileSize", 0);
if (connectionId.isEmpty() || tableName.isEmpty() || columnName.isEmpty() || fileName.isEmpty()) {
callback.failure(400, new JSONObject().put("status","error").put("message","参数不完整").toString());
break;
}
// 在实际应用中,这里应该处理文件上传
// 由于CEF的限制文件上传可能需要通过其他方式处理
// 这里提供一个模拟实现
try {
// 获取文件存储设置
JSONObject fileSettings = getFileSettings(connectionId, tableName);
String storageType = fileSettings.optString("storageType", "filesystem");
String storagePath = fileSettings.optString("storagePath", "./uploads");
long maxFileSize = fileSettings.optLong("maxFileSize", 10 * 1024 * 1024); // 默认10MB
JSONArray allowedTypes = fileSettings.optJSONArray("allowedTypes");
// 检查文件大小
if (fileSize > maxFileSize) {
callback.failure(400, new JSONObject().put("status","error")
.put("message","文件大小超过限制: " + (maxFileSize/1024/1024) + "MB").toString());
break;
}
// 检查文件类型
if (allowedTypes != null && allowedTypes.length() > 0) {
String fileExt = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
boolean allowed = false;
// 简化文件类型检查
for (int i = 0; i < allowedTypes.length(); i++) {
String type = allowedTypes.getString(i);
if (("image".equals(type) && isImageFile(fileExt)) ||
("document".equals(type) && isDocumentFile(fileExt)) ||
("audio".equals(type) && isAudioFile(fileExt)) ||
("video".equals(type) && isVideoFile(fileExt)) ||
("archive".equals(type) && isArchiveFile(fileExt))) {
allowed = true;
break;
}
}
if (!allowed) {
callback.failure(400, new JSONObject().put("status","error")
.put("message","不允许的文件类型").toString());
break;
}
}
// 生成文件路径
String fileId = "file_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
String fileExtension = fileName.substring(fileName.lastIndexOf('.'));
String filePath;
if ("database".equals(storageType)) {
// 存储在数据库中 - 返回文件ID
filePath = fileId;
} else {
// 存储在文件系统中
java.nio.file.Path uploadDir = java.nio.file.Paths.get(storagePath);
try {
java.nio.file.Files.createDirectories(uploadDir);
} catch (Exception e) {
// 忽略创建目录失败
}
filePath = uploadDir.resolve(fileId + fileExtension).toString();
// 在实际应用中,这里应该保存文件到指定路径
// 由于CEF限制这里只是模拟
}
JSONObject resp = new JSONObject();
resp.put("status", "success");
resp.put("fileId", fileId);
resp.put("path", filePath);
resp.put("message", "文件上传成功");
callback.success(resp.toString());
} catch (Exception ex) {
JSONObject err = new JSONObject();
err.put("status", "error");
err.put("message", "文件上传失败: " + ex.getMessage());
callback.failure(500, err.toString());
}
break;
}
*/
default: {
JSONObject err = new JSONObject();
err.put("status", "error");
@@ -1263,7 +1747,11 @@ public class MainApplication {
// 处理获取表结构
private static void handleGetTableStructure(JSONObject request, CefQueryCallback callback) {
Connection connection = null;
ResultSet resultSet = null;
ResultSet rs = null;
ResultSet idxRs = null;
ResultSet pkRs = null;
ResultSet fkRs = null;
PreparedStatement ps = null;
try {
String tableName = request.optString("tableName", "");
@@ -1287,32 +1775,166 @@ public class MainApplication {
String catalog = null;
String schema = null;
switch (info.driver.toLowerCase()) {
case "mysql":
catalog = info.database;
break;
case "postgresql":
schema = "public";
break;
if (info != null && info.driver != null) {
switch (info.driver.toLowerCase()) {
case "mysql":
catalog = info.database;
break;
case "postgresql":
schema = "public";
break;
default:
// leave null
}
}
resultSet = metaData.getColumns(catalog, schema, tableName, null);
// 读取列信息
rs = metaData.getColumns(catalog, schema, tableName, null);
JSONArray columns = new JSONArray();
while (resultSet.next()) {
while (rs.next()) {
JSONObject column = new JSONObject();
column.put("name", resultSet.getString("COLUMN_NAME"));
column.put("type", resultSet.getString("TYPE_NAME"));
column.put("size", resultSet.getInt("COLUMN_SIZE"));
column.put("nullable", resultSet.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
column.put("defaultValue", resultSet.getString("COLUMN_DEF"));
String rawType = rs.getString("TYPE_NAME"); // eg. VARCHAR, INT, DECIMAL
int colSize = rs.getInt("COLUMN_SIZE");
int decimalDigits = 0;
try { decimalDigits = rs.getInt("DECIMAL_DIGITS"); } catch (Exception ignored) {}
// 生成更友好的类型展示(匹配前端类型选项)
String displayType = rawType != null ? rawType : "";
if (rawType != null) {
String rt = rawType.toUpperCase();
if (rt.equals("VARCHAR") || rt.equals("CHAR")) {
displayType = rt + "(" + colSize + ")";
} else if (rt.equals("DECIMAL") || rt.equals("NUMERIC")) {
displayType = "DECIMAL(" + colSize + "," + decimalDigits + ")";
} else if (rt.equals("DOUBLE") || rt.equals("FLOAT")) {
// 保持为原始类型
displayType = rt;
} else {
// 有时候 TYPE_NAME 已经包含长度(如 VARCHAR(255)),保留原样
displayType = rawType;
}
}
column.put("name", rs.getString("COLUMN_NAME"));
column.put("type", displayType);
column.put("rawType", rawType);
column.put("size", colSize);
column.put("decimalDigits", decimalDigits);
int nullableFlag = rs.getInt("NULLABLE");
column.put("nullable", nullableFlag == DatabaseMetaData.columnNullable);
column.put("defaultValue", rs.getString("COLUMN_DEF"));
// 尝试获取是否自增(部分驱动返回 IS_AUTOINCREMENT
try {
String isAuto = rs.getString("IS_AUTOINCREMENT");
column.put("autoIncrement", "YES".equalsIgnoreCase(isAuto));
} catch (Exception ignored) {
// ignore
}
columns.put(column);
}
if (rs != null) { rs.close(); rs = null; }
// 读取主键(用于在 constraints 中显示)
JSONArray constraints = new JSONArray();
pkRs = metaData.getPrimaryKeys(catalog, schema, tableName);
List<String> pkColumns = new ArrayList<>();
while (pkRs != null && pkRs.next()) {
String pkName = pkRs.getString("PK_NAME");
String pkCol = pkRs.getString("COLUMN_NAME");
if (pkCol != null) pkColumns.add(pkCol);
}
if (!pkColumns.isEmpty()) {
JSONObject pkObj = new JSONObject();
pkObj.put("name", "PRIMARY");
pkObj.put("type", "PRIMARY KEY");
pkObj.put("definition", "PRIMARY KEY (" + String.join(",", pkColumns) + ")");
constraints.put(pkObj);
}
if (pkRs != null) { pkRs.close(); pkRs = null; }
// 读取外键
fkRs = metaData.getImportedKeys(catalog, schema, tableName);
while (fkRs != null && fkRs.next()) {
String fkName = fkRs.getString("FK_NAME");
String fkCol = fkRs.getString("FKCOLUMN_NAME");
String pkTable = fkRs.getString("PKTABLE_NAME");
String pkCol = fkRs.getString("PKCOLUMN_NAME");
JSONObject fkObj = new JSONObject();
fkObj.put("name", fkName == null ? ("fk_" + fkCol) : fkName);
fkObj.put("type", "FOREIGN KEY");
fkObj.put("definition", String.format("FOREIGN KEY (%s) REFERENCES %s(%s)", fkCol, pkTable, pkCol));
constraints.put(fkObj);
}
if (fkRs != null) { fkRs.close(); fkRs = null; }
// 读取索引信息(不重复合并同名索引的列)
idxRs = metaData.getIndexInfo(catalog, schema, tableName, false, false);
Map<String, JSONObject> idxMap = new LinkedHashMap<>();
while (idxRs != null && idxRs.next()) {
String idxName = idxRs.getString("INDEX_NAME");
if (idxName == null) continue; // driver-specific
boolean nonUnique = idxRs.getBoolean("NON_UNIQUE");
String colName = idxRs.getString("COLUMN_NAME");
if (!idxMap.containsKey(idxName)) {
JSONObject idxObj = new JSONObject();
idxObj.put("name", idxName);
idxObj.put("type", nonUnique ? "INDEX" : "UNIQUE");
idxObj.put("columns", colName == null ? "" : colName);
idxMap.put(idxName, idxObj);
} else {
JSONObject idxObj = idxMap.get(idxName);
String prevCols = idxObj.optString("columns", "");
if (colName != null && !prevCols.contains(colName)) {
if (prevCols.isEmpty()) idxObj.put("columns", colName);
else idxObj.put("columns", prevCols + "," + colName);
}
}
}
JSONArray indexes = new JSONArray();
for (JSONObject v : idxMap.values()) indexes.put(v);
if (idxRs != null) { idxRs.close(); idxRs = null; }
// 表级信息MySQL: ENGINE, COLLATION, COMMENT
String engine = null;
String collation = null;
String charset = null;
String tableComment = null;
if (info != null && info.driver != null && "mysql".equalsIgnoreCase(info.driver) && info.database != null) {
String sql = "SELECT ENGINE, TABLE_COLLATION, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?";
try {
ps = connection.prepareStatement(sql);
ps.setString(1, info.database);
ps.setString(2, tableName);
ResultSet tRs = ps.executeQuery();
if (tRs.next()) {
engine = tRs.getString("ENGINE");
collation = tRs.getString("TABLE_COLLATION");
tableComment = tRs.getString("TABLE_COMMENT");
if (collation != null && collation.contains("_")) {
charset = collation.split("_")[0];
}
}
if (tRs != null) { tRs.close(); tRs = null; }
} catch (Exception ignored) {
// 忽略信息 schema 查询失败的情况(可能权限或 driver 不支持)
} finally {
if (ps != null) { try { ps.close(); } catch (SQLException ignored) {} ps = null; }
}
}
JSONObject response = new JSONObject();
response.put("status", "success");
response.put("tableName", tableName);
response.put("engine", engine == null ? JSONObject.NULL : engine);
response.put("charset", charset == null ? JSONObject.NULL : charset);
response.put("collation", collation == null ? JSONObject.NULL : collation);
response.put("comment", tableComment == null ? JSONObject.NULL : tableComment);
response.put("columns", columns);
response.put("indexes", indexes);
response.put("constraints", constraints);
callback.success(response.toString());
} catch (Exception e) {
@@ -1321,29 +1943,33 @@ public class MainApplication {
error.put("message", "获取表结构失败: " + e.getMessage());
callback.failure(500, error.toString());
} finally {
try {
if (resultSet != null) resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
try { if (rs != null) rs.close(); } catch (SQLException ignored) {}
try { if (idxRs != null) idxRs.close(); } catch (SQLException ignored) {}
try { if (pkRs != null) pkRs.close(); } catch (SQLException ignored) {}
try { if (fkRs != null) fkRs.close(); } catch (SQLException ignored) {}
try { if (ps != null) ps.close(); } catch (SQLException ignored) {}
}
}
// 处理主题更新(保持不变)
// 这里自己实现了一个一般使用事件实现
private static void handleUpdateTheme(JSONObject request, CefQueryCallback callback) {
try {
String theme = request.optString("theme", "light");
// 通过 AxisInnovatorsBox 获取当前主题状态
boolean isDarkMode = AxisInnovatorsBox.getMain().getRegistrationTopic().isDarkMode();
String theme = isDarkMode ? "dark" : "light";
JSONObject response = new JSONObject();
response.put("status", "success");
response.put("theme", theme);
response.put("message", "主题已更新为: " + theme);
response.put("message", "当前主题: " + theme);
callback.success(response.toString());
} catch (Exception e) {
JSONObject error = new JSONObject();
error.put("status", "error");
error.put("message", "主题更新失败: " + e.getMessage());
error.put("message", "获取主题失败: " + e.getMessage());
callback.failure(500, error.toString());
}
}

View File

@@ -80,6 +80,9 @@ public class DatabaseConnectionManager {
case "mysql":
props.setProperty("useSSL", "false");
props.setProperty("serverTimezone", "UTC");
props.setProperty("allowPublicKeyRetrieval", "true");
props.setProperty("useUnicode", "true");
props.setProperty("characterEncoding", "UTF-8");
connection = DriverManager.getConnection(url, props);
break;
case "postgresql":