diff --git a/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseDBManage.java b/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseDBManage.java index 92822099..cf1797c5 100644 --- a/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/ClickHouseDBManage.java @@ -10,6 +10,12 @@ import java.sql.*; import java.util.Objects; public class ClickHouseDBManage extends DefaultDBManage implements DBManage { + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2DBManage.java b/chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2DBManage.java index d5827248..a3035a31 100644 --- a/chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2DBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-db2/src/main/java/ai/chat2db/plugin/db2/DB2DBManage.java @@ -17,6 +17,13 @@ import java.util.Objects; public class DB2DBManage extends DefaultDBManage implements DBManage { + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } + @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMDBManage.java b/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMDBManage.java index 83381ccf..71334813 100644 --- a/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-dm/src/main/java/ai/chat2db/plugin/dm/DMDBManage.java @@ -23,6 +23,12 @@ public class DMDBManage extends DefaultDBManage implements DBManage { = "SELECT OWNER, TRIGGER_NAME, TABLE_OWNER, TABLE_NAME, TRIGGERING_TYPE, TRIGGERING_EVENT, STATUS, TRIGGER_BODY " + "FROM ALL_TRIGGERS WHERE OWNER = '%s' AND TRIGGER_NAME = '%s'"; + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, schemaName,tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2DBManage.java b/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2DBManage.java index b558bca7..334e24e8 100644 --- a/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2DBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2DBManage.java @@ -10,9 +10,18 @@ import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.Objects; public class H2DBManage extends DefaultDBManage implements DBManage { + + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); @@ -36,6 +45,28 @@ public class H2DBManage extends DefaultDBManage implements DBManage { } } + private void exportTableData(Connection connection, String tableName, StringBuilder sqlBuilder) throws SQLException { + String sql = String.format("select * from %s", tableName); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + ResultSetMetaData metaData = resultSet.getMetaData(); + while (resultSet.next()) { + sqlBuilder.append("INSERT INTO ").append(tableName).append(" VALUES ("); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + String value = resultSet.getString(i); + if (Objects.isNull(value)) { + sqlBuilder.append("NULL"); + } else { + sqlBuilder.append("'").append(value).append("'"); + } + if (i < metaData.getColumnCount()) { + sqlBuilder.append(", "); + } + } + sqlBuilder.append(");\n"); + } + sqlBuilder.append("\n"); + } + } @Override public void connectDatabase(Connection connection, String database) { diff --git a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlDBManage.java b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlDBManage.java index 46ffcd9a..b494d64e 100644 --- a/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-mysql/src/main/java/ai/chat2db/plugin/mysql/MysqlDBManage.java @@ -10,6 +10,12 @@ import java.sql.*; import java.util.Objects; public class MysqlDBManage extends DefaultDBManage implements DBManage { + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleDBManage.java b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleDBManage.java index 6722bf68..728177b1 100644 --- a/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-oracle/src/main/java/ai/chat2db/plugin/oracle/OracleDBManage.java @@ -27,6 +27,12 @@ public class OracleDBManage extends DefaultDBManage implements DBManage { private static String TRIGGER_DDL_SQL = "SELECT DBMS_METADATA.GET_DDL('TRIGGER', trigger_name) AS ddl FROM all_triggers WHERE owner = '%s' AND trigger_name = '%s'"; private static String FUNCTION_DDL_SQL = "SELECT DBMS_METADATA.GET_DDL('FUNCTION', object_name) as ddl FROM all_procedures WHERE owner = '%s' AND object_name = '%s'"; + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); exportTables(connection, schemaName, sqlBuilder, containData); diff --git a/chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLDBManage.java b/chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLDBManage.java index d6be207a..55b59c37 100644 --- a/chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-postgresql/src/main/java/ai/chat2db/plugin/postgresql/PostgreSQLDBManage.java @@ -15,7 +15,12 @@ import static ai.chat2db.plugin.postgresql.consts.SQLConst.*; public class PostgreSQLDBManage extends DefaultDBManage implements DBManage { - + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection,schemaName, tableName, sqlBuilder); + return sqlBuilder.toString(); + } public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); exportTypes(connection, sqlBuilder); diff --git a/chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteDBManage.java b/chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteDBManage.java index 3a4d4814..61c2ee96 100644 --- a/chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-sqlite/src/main/java/ai/chat2db/plugin/sqlite/SqliteDBManage.java @@ -11,7 +11,12 @@ import java.util.Objects; public class SqliteDBManage extends DefaultDBManage implements DBManage { - + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerDBManage.java b/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerDBManage.java index 9fb8181b..8e9bff5d 100644 --- a/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerDBManage.java +++ b/chat2db-server/chat2db-plugins/chat2db-sqlserver/src/main/java/ai/chat2db/plugin/sqlserver/SqlServerDBManage.java @@ -41,6 +41,12 @@ public class SqlServerDBManage extends DefaultDBManage implements DBManage { + "triggerDefinition, CASE WHEN status & 1 = 1 THEN 'Enabled' ELSE 'Disabled' END AS Status FROM sysobjects " + "WHERE xtype = 'TR' "; + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + StringBuilder sqlBuilder = new StringBuilder(); + exportTableData(connection, tableName, sqlBuilder); + return sqlBuilder.toString(); + } @Override public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { StringBuilder sqlBuilder = new StringBuilder(); diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportFileSuffix.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportFileSuffix.java index 9f98f0cd..9b5d854e 100644 --- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportFileSuffix.java +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportFileSuffix.java @@ -19,7 +19,15 @@ public enum ExportFileSuffix { //html HTML(".html"), //pdf - PDF(".pdf"); + PDF(".pdf"), + + SQL(".sql"), + + JSON(".json"), + + CSV(".csv"), + + ZIP(".zip"); private String suffix; diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportTypeEnum.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportTypeEnum.java index c00ac9bb..89daeae1 100644 --- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportTypeEnum.java +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/enums/ExportTypeEnum.java @@ -45,7 +45,11 @@ public enum ExportTypeEnum implements BaseEnum { /** * PDF */ - PDF("PDF"); + PDF("PDF"), + + JSON("JSON"), + + SQL("SQL"); final String description; diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseExportDataParam.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseExportDataParam.java new file mode 100644 index 00000000..4e562ced --- /dev/null +++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-api/src/main/java/ai/chat2db/server/domain/api/param/datasource/DatabaseExportDataParam.java @@ -0,0 +1,18 @@ +package ai.chat2db.server.domain.api.param.datasource; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: zgq + * @date: 2024年03月24日 13:17 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DatabaseExportDataParam { + private String databaseName; + private String schemaName; + private String exportType; +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/DatabaseController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/DatabaseController.java index 61b4c45c..6348db87 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/DatabaseController.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/DatabaseController.java @@ -2,6 +2,7 @@ package ai.chat2db.server.web.api.controller.rdb; import ai.chat2db.server.domain.api.param.MetaDataQueryParam; import ai.chat2db.server.domain.api.param.datasource.DatabaseCreateParam; +import ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam; import ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam; import ai.chat2db.server.domain.api.param.datasource.DatabaseQueryAllParam; import ai.chat2db.server.domain.api.service.DatabaseService; @@ -13,7 +14,10 @@ import ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRe import ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO; import ai.chat2db.server.web.api.controller.rdb.converter.DatabaseConverter; import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter; +import ai.chat2db.server.web.api.controller.rdb.data.export.strategy.ExportDBDataStrategy; +import ai.chat2db.server.web.api.controller.rdb.factory.ExportDBDataStrategyFactory; import ai.chat2db.server.web.api.controller.rdb.request.DatabaseCreateRequest; +import ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportDataRequest; import ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportRequest; import ai.chat2db.server.web.api.controller.rdb.request.UpdateDatabaseRequest; import ai.chat2db.server.web.api.controller.rdb.vo.MetaSchemaVO; @@ -27,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.io.PrintWriter; +import java.lang.reflect.Constructor; import java.util.Objects; /** @@ -123,4 +128,19 @@ public class DatabaseController { throw new RuntimeException(e); } } + + @PostMapping("/export_data") + public void exportData(@Valid @RequestBody DatabaseExportDataRequest request, HttpServletResponse response) { + Class targetClass = ExportDBDataStrategyFactory.get(request.getExportType()); + response.setCharacterEncoding("utf-8"); + DatabaseExportDataParam param = databaseConverter.request2param(request); + try { + Constructor constructor = targetClass.getDeclaredConstructor(); + ExportDBDataStrategy service = (ExportDBDataStrategy) constructor.newInstance(); + service.doExport(param, response); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } } diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/DatabaseConverter.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/DatabaseConverter.java index b4da24b7..bb9bc4a9 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/DatabaseConverter.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/DatabaseConverter.java @@ -1,7 +1,9 @@ package ai.chat2db.server.web.api.controller.rdb.converter; +import ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam; import ai.chat2db.server.domain.api.param.datasource.DatabaseExportParam; import ai.chat2db.server.web.api.controller.rdb.request.DatabaseCreateRequest; +import ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportDataRequest; import ai.chat2db.server.web.api.controller.rdb.request.DatabaseExportRequest; import ai.chat2db.spi.model.Database; import org.mapstruct.Mapper; @@ -12,4 +14,6 @@ public abstract class DatabaseConverter { public abstract Database request2param(DatabaseCreateRequest request); public abstract DatabaseExportParam request2param(DatabaseExportRequest request); + + public abstract DatabaseExportDataParam request2param(DatabaseExportDataRequest request); } diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2CsvStrategy.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2CsvStrategy.java new file mode 100644 index 00000000..d091bf0c --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2CsvStrategy.java @@ -0,0 +1,54 @@ +package ai.chat2db.server.web.api.controller.rdb.data.export.strategy; + +import ai.chat2db.server.domain.api.enums.ExportFileSuffix; +import ai.chat2db.spi.util.ResultSetUtils; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; + +import java.io.ByteArrayOutputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ExportDBData2CsvStrategy extends ExportDBDataStrategy { + + public ExportDBData2CsvStrategy() { + suffix = ExportFileSuffix.CSV.getSuffix(); + contentType = "application/zip"; + } + + @Override + protected ByteArrayOutputStream exportData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + String sql; + if (Objects.isNull(schemaName)) { + sql = String.format("select * from %s", tableName); + } else { + sql = String.format("select * from %s.%s", schemaName, tableName); + } + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + ResultSetMetaData metaData = resultSet.getMetaData(); + List> headList = ResultSetUtils.getRsHeader(resultSet) + .stream() + .map(Collections::singletonList) + .collect(Collectors.toList()); + int columnCount = metaData.getColumnCount(); + List> dataList = new ArrayList<>(); + while (resultSet.next()) { + List row = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + row.add(resultSet.getString(i)); + } + dataList.add(row); + } + EasyExcel.write(byteOut).excelType(ExcelTypeEnum.CSV).sheet(tableName).head(headList).doWrite(dataList); + } + return byteOut; + } +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2ExcelStrategy.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2ExcelStrategy.java new file mode 100644 index 00000000..b8fc38ee --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2ExcelStrategy.java @@ -0,0 +1,53 @@ +package ai.chat2db.server.web.api.controller.rdb.data.export.strategy; + +import ai.chat2db.server.domain.api.enums.ExportFileSuffix; +import ai.chat2db.spi.util.ResultSetUtils; +import com.alibaba.excel.EasyExcel; + +import java.io.ByteArrayOutputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ExportDBData2ExcelStrategy extends ExportDBDataStrategy { + + public ExportDBData2ExcelStrategy() { + suffix = ExportFileSuffix.EXCEL.getSuffix(); + contentType = "application/zip"; + } + + @Override + protected ByteArrayOutputStream exportData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + String sql; + if (Objects.isNull(schemaName)) { + sql = String.format("select * from %s", tableName); + } else { + sql = String.format("select * from %s.%s", schemaName, tableName); + } + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + List> headList = ResultSetUtils.getRsHeader(resultSet) + .stream() + .map(Collections::singletonList) + .collect(Collectors.toList()); + List> dataList = new ArrayList<>(); + while (resultSet.next()) { + List row = new ArrayList<>(); + for (int i = 1; i <= columnCount; i++) { + row.add(resultSet.getString(i)); + } + dataList.add(row); + } + EasyExcel.write(byteOut).sheet(tableName).head(headList).doWrite(dataList); + } + return byteOut; + } +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2JsonStrategy.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2JsonStrategy.java new file mode 100644 index 00000000..971c23b5 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2JsonStrategy.java @@ -0,0 +1,60 @@ +package ai.chat2db.server.web.api.controller.rdb.data.export.strategy; + +import ai.chat2db.server.domain.api.enums.ExportFileSuffix; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.*; + +public class ExportDBData2JsonStrategy extends ExportDBDataStrategy { + + public ExportDBData2JsonStrategy() { + suffix = ExportFileSuffix.JSON.getSuffix(); + contentType = "application/zip"; + } + + @Override + protected ByteArrayOutputStream exportData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteOut, StandardCharsets.UTF_8))) { + String sql; + if (Objects.isNull(schemaName)) { + sql = String.format("SELECT * FROM %s", tableName); + } else { + sql = String.format("SELECT * FROM %s.%s", schemaName, tableName); + } + try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) { + ResultSetMetaData metaData = resultSet.getMetaData(); + int columnCount = metaData.getColumnCount(); + List> data = new ArrayList<>(); + + while (resultSet.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(metaData.getColumnName(i), resultSet.getObject(i)); + } + data.add(row); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String jsonString = objectMapper.writeValueAsString(data); + writer.println(jsonString); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return byteOut; + } + + +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2SqlStrategy.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2SqlStrategy.java new file mode 100644 index 00000000..1e057747 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBData2SqlStrategy.java @@ -0,0 +1,33 @@ +package ai.chat2db.server.web.api.controller.rdb.data.export.strategy; + +import ai.chat2db.server.domain.api.enums.ExportFileSuffix; +import ai.chat2db.spi.sql.Chat2DBContext; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author: zgq + * @date: 2024年03月24日 12:50 + */ +public class ExportDBData2SqlStrategy extends ExportDBDataStrategy { + + public ExportDBData2SqlStrategy() { + suffix = ExportFileSuffix.SQL.getSuffix(); + contentType = "application/zip"; + } + + @Override + protected ByteArrayOutputStream exportData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteOut, StandardCharsets.UTF_8))) { + String sql = Chat2DBContext.getDBManage().exportDatabaseData(connection, databaseName, schemaName, tableName); + writer.println(sql); + } + return byteOut; + } +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBDataStrategy.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBDataStrategy.java new file mode 100644 index 00000000..cb4a1820 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/data/export/strategy/ExportDBDataStrategy.java @@ -0,0 +1,65 @@ +package ai.chat2db.server.web.api.controller.rdb.data.export.strategy; + +import ai.chat2db.server.domain.api.enums.ExportFileSuffix; +import ai.chat2db.server.domain.api.param.datasource.DatabaseExportDataParam; +import ai.chat2db.spi.sql.Chat2DBContext; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.io.ByteArrayOutputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * @author: zgq + * @date: 2024年03月24日 12:46 + */ +@Getter +@AllArgsConstructor +@NoArgsConstructor +public abstract class ExportDBDataStrategy { + + public String suffix; + public String contentType; + + public void doExport(DatabaseExportDataParam param, HttpServletResponse response) { + String databaseName = param.getDatabaseName(); + String schemaName = param.getSchemaName(); + setResponseHeaders(param, response); + try (ServletOutputStream outputStream = response.getOutputStream(); + ZipOutputStream zipOut = new ZipOutputStream(outputStream); + Connection connection = Chat2DBContext.getConnection();) { + List tableNames = Chat2DBContext.getMetaData().tableNames(connection, databaseName, schemaName, null); + tableNames.addAll(Chat2DBContext.getMetaData().viewNames(connection, databaseName, schemaName)); + for (String tableName : tableNames) { + String fileName = tableName + getSuffix(); + zipOut.putNextEntry(new ZipEntry(fileName)); + ByteArrayOutputStream byteOut = exportData(connection, databaseName, schemaName, tableName); + byteOut.writeTo(zipOut); + zipOut.closeEntry(); + byteOut.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void setResponseHeaders(DatabaseExportDataParam param, HttpServletResponse response) { + response.setContentType(contentType); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + getFileName(param) + ExportFileSuffix.ZIP.getSuffix()); + } + + protected String getFileName(DatabaseExportDataParam param) { + return Objects.isNull(param.getSchemaName()) ? param.getDatabaseName() : param.getSchemaName(); + } + + protected abstract ByteArrayOutputStream exportData(Connection connection, String databaseName, String schemaName, String tableName) throws SQLException; + +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/factory/ExportDBDataStrategyFactory.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/factory/ExportDBDataStrategyFactory.java new file mode 100644 index 00000000..638557b1 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/factory/ExportDBDataStrategyFactory.java @@ -0,0 +1,31 @@ +package ai.chat2db.server.web.api.controller.rdb.factory; + +import ai.chat2db.server.domain.api.enums.ExportTypeEnum; +import ai.chat2db.server.web.api.controller.rdb.data.export.strategy.*; +import lombok.SneakyThrows; + +import java.util.Map; + +/** + * @author: zgq + * @date: 2024年03月24日 12:53 + */ +public class ExportDBDataStrategyFactory { + + public static final Map> SERVICE_MAP = Map.of( + ExportTypeEnum.SQL.getCode(), ExportDBData2SqlStrategy.class, + ExportTypeEnum.CSV.getCode(), ExportDBData2CsvStrategy.class, + ExportTypeEnum.EXCEL.getCode(), ExportDBData2ExcelStrategy.class, + ExportTypeEnum.JSON.getCode(), ExportDBData2JsonStrategy.class + ); + + @SneakyThrows + public static Class get(String type) { + Class dataResult = SERVICE_MAP.get(type); + if (dataResult == null) { + throw new ClassNotFoundException("no ExportUI was found"); + } else { + return dataResult; + } + } +} \ No newline at end of file diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseExportDataRequest.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseExportDataRequest.java new file mode 100644 index 00000000..41686b50 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/request/DatabaseExportDataRequest.java @@ -0,0 +1,19 @@ +package ai.chat2db.server.web.api.controller.rdb.request; + +import ai.chat2db.server.web.api.controller.data.source.request.DataSourceBaseRequest; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: zgq + * @date: 2024年03月24日 12:36 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DatabaseExportDataRequest extends DataSourceBaseRequest { + @NotNull + private String exportType; +} \ No newline at end of file diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/DBManage.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/DBManage.java index 32aa4225..eae7e20a 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/DBManage.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/DBManage.java @@ -122,4 +122,6 @@ public interface DBManage { void updateProcedure(Connection connection, @NotEmpty String databaseName, String schemaName, @NotNull Procedure procedure) throws SQLException; String exportDatabase(Connection connection, String databaseName, String schemaName,boolean containData) throws SQLException; + + String exportDatabaseData(Connection connection, String databaseName, String schemaName,String tableName) throws SQLException; } \ No newline at end of file diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/MetaData.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/MetaData.java index 196164ff..e6dc2bde 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/MetaData.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/MetaData.java @@ -54,6 +54,16 @@ public interface MetaData { */ List tables(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName); + /** + * Querying all table name under a schema. + * @param connection + * @param databaseName + * @param schemaName + * @param tableName + * @return + */ + List tableNames(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName); + /** * Querying all table under a schema. @@ -79,6 +89,14 @@ public interface MetaData { Table view(Connection connection, @NotEmpty String databaseName, String schemaName, String viewName); + /** query view names + * @param connection + * @param databaseName + * @param schemaName + * @return + */ + List viewNames(Connection connection, @NotEmpty String databaseName, String schemaName); + /** * Querying all views under a schema. * diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultDBManage.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultDBManage.java index 9d0a3c26..4c9d5378 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultDBManage.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultDBManage.java @@ -142,7 +142,10 @@ public class DefaultDBManage implements DBManage { public String exportDatabase(Connection connection, String databaseName, String schemaName, boolean containData) throws SQLException { return null; } - + @Override + public String exportDatabaseData(Connection connection, String databaseName, String schemaName,String tableName) throws SQLException { + return null; + } @Override public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) { diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java index 6a771095..a84d43df 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/jdbc/DefaultMetaService.java @@ -1,10 +1,5 @@ package ai.chat2db.spi.jdbc; -import java.sql.Connection; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - import ai.chat2db.server.tools.base.wrapper.result.PageResult; import ai.chat2db.spi.CommandExecutor; import ai.chat2db.spi.MetaData; @@ -16,6 +11,11 @@ import com.google.common.collect.Lists; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import java.sql.Connection; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + /** * @author jipengfei * @version : DefaultMetaService.java @@ -29,9 +29,9 @@ public class DefaultMetaService implements MetaData { @Override public List schemas(Connection connection, String databaseName) { List schemas = SQLExecutor.getInstance().schemas(connection, databaseName, null); - if(StringUtils.isNotBlank(databaseName) && CollectionUtils.isNotEmpty(schemas)){ - for ( Schema schema : schemas) { - if(StringUtils.isBlank(schema.getDatabaseName())){ + if (StringUtils.isNotBlank(databaseName) && CollectionUtils.isNotEmpty(schemas)) { + for (Schema schema : schemas) { + if (StringUtils.isBlank(schema.getDatabaseName())) { schema.setDatabaseName(databaseName); } } @@ -46,14 +46,19 @@ public class DefaultMetaService implements MetaData { @Override public List
tables(Connection connection, String databaseName, String schemaName, String tableName) { - return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{"TABLE","SYSTEM TABLE"}); + return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{"TABLE", "SYSTEM TABLE"}); + } + + @Override + public List tableNames(Connection connection, String databaseName, String schemaName, String tableName) { + return SQLExecutor.getInstance().tableNames(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, tableName, new String[]{"TABLE", "SYSTEM TABLE"}); } @Override public PageResult
tables(Connection connection, String databaseName, String schemaName, String tableNamePattern, int pageNo, int pageSize) { List
tables = tables(connection, databaseName, schemaName, tableNamePattern); - if(CollectionUtils.isEmpty(tables)){ - return PageResult.of(tables,0L,pageNo, pageSize); + if (CollectionUtils.isEmpty(tables)) { + return PageResult.of(tables, 0L, pageNo, pageSize); } List result = tables.stream().skip((pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList()); return PageResult.of(result, (long) tables.size(), pageNo, pageSize); @@ -69,10 +74,15 @@ public class DefaultMetaService implements MetaData { return SQLExecutor.getInstance().tables(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, null, new String[]{"VIEW"}); } + @Override + public List viewNames(Connection connection, String databaseName, String schemaName) { + return SQLExecutor.getInstance().tableNames(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName, null, new String[]{"VIEW"}); + } + @Override public List functions(Connection connection, String databaseName, String schemaName) { List functions = SQLExecutor.getInstance().functions(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName); - if(CollectionUtils.isEmpty(functions)){ + if (CollectionUtils.isEmpty(functions)) { return functions; } return functions.stream().filter(function -> StringUtils.isNotBlank(function.getFunctionName())).map(function -> { @@ -89,9 +99,9 @@ public class DefaultMetaService implements MetaData { @Override public List procedures(Connection connection, String databaseName, String schemaName) { - List procedures = SQLExecutor.getInstance().procedures(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName); + List procedures = SQLExecutor.getInstance().procedures(connection, StringUtils.isEmpty(databaseName) ? null : databaseName, StringUtils.isEmpty(schemaName) ? null : schemaName); - if(CollectionUtils.isEmpty(procedures)){ + if (CollectionUtils.isEmpty(procedures)) { return procedures; } return procedures.stream().filter(function -> StringUtils.isNotBlank(function.getProcedureName())).map(procedure -> { diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java index cb5881b2..c22db1f4 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/sql/SQLExecutor.java @@ -440,6 +440,26 @@ public class SQLExecutor implements CommandExecutor { } } + /** query table names + * @param connection + * @param databaseName + * @param schemaName + * @param tableName + * @param types + * @return + */ + public List tableNames(Connection connection, String databaseName, String schemaName, String tableName, String[] types) { + List tableNames = new ArrayList<>(); + try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, tableName, types)) { + while (resultSet.next()) { + tableNames.add(resultSet.getString("TABLE_NAME")); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return tableNames; + } + /** * Get all database table columns * diff --git a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java index aef38091..c308d355 100644 --- a/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java +++ b/chat2db-server/chat2db-spi/src/main/java/ai/chat2db/spi/util/ResultSetUtils.java @@ -23,7 +23,7 @@ public class ResultSetUtils { - private static List getRsHeader(ResultSet rs) { + public static List getRsHeader(ResultSet rs) { try { ResultSetMetaData resultSetMetaData = rs.getMetaData(); int col = resultSetMetaData.getColumnCount();