From 031157361cf45b51940b692fffe2a7e403989610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 09:14:54 +0800 Subject: [PATCH 1/8] refine --- note.txt | 3 + pom.xml | 6 ++ .../com/zwc/sqldataprocessor/SqlExecutor.java | 68 +++++++++++++------ .../zwc/sqldataprocessor/SqlFileExecutor.java | 2 +- .../com/zwc/sqldataprocessor/SqlLoader.java | 12 ++++ .../zwc/sqldataprocessor/entity/DataList.java | 21 ++++++ .../com/zwc/sqldataprocessor/entity/Sql.java | 1 + 7 files changed, 91 insertions(+), 22 deletions(-) diff --git a/note.txt b/note.txt index 413a9a7..e3b28d4 100644 --- a/note.txt +++ b/note.txt @@ -1 +1,4 @@ 待开发功能: + 针对常见的错误, 直接在控制台显示具体的错误, 不需要打印调用栈 + 支持临时表的模式, 防止临时表占用超出string大小. + 支持导出格式: xlsx, csv. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 16d7488..0e11108 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,12 @@ 3.4 + + org.apache.commons + commons-collections4 + 4.4 + + org.apache.poi poi diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java index 46d2bbe..75440b0 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java @@ -6,8 +6,10 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.zwc.sqldataprocessor.entity.DataList; import com.zwc.sqldataprocessor.entity.DataList.ColumnType; @@ -15,8 +17,14 @@ public class SqlExecutor { - public static DataList exec(String sql, String databaseName, Map tables) { - String rawSql = renderSql(sql, tables, databaseName); + public static DataList exec(String sql, String databaseName, Map tables, boolean useTempTables) { + String rawSql = renderSql(sql, tables, databaseName, useTempTables); + + // 解除group_concat的长度限制 + if (DatabaseConfigLoader.isMySql(databaseName)) { + execRawSql("SET SESSION group_concat_max_len = 1000000;", databaseName); + } + return execRawSql(rawSql, databaseName); } @@ -28,13 +36,6 @@ public static DataList execRawSql(String sql, String databaseName) { Connection conn = DatabaseConfigLoader.getConn(databaseName); - // 解除group_concat的长度限制 - if (DatabaseConfigLoader.isMySql(databaseName)) { - try (Statement cmd = conn.createStatement()) { - cmd.execute("SET SESSION group_concat_max_len = 1000000;"); - } - } - try (Statement cmd = conn.createStatement()) { boolean hasResult = cmd.execute(sql); @@ -116,7 +117,8 @@ static DataList readResult(ResultSet resultSet) throws SQLException { return table; } - static String renderSql(String sql, Map tables, String databaseName) { + static String renderSql(String sql, Map tables, String databaseName, boolean useTempTables) { + Set createdTempTables = new HashSet<>(); StringBuilder sqlBuilder = new StringBuilder(); for (String sqlLine : sql.split("\n")) { @@ -143,20 +145,36 @@ static String renderSql(String sql, Map tables, String databas throw new RuntimeException("结果集$" + tableName + "不存在."); } - // 构建数据集sql语句 - StringBuilder builder = new StringBuilder(); - builder.append("(\n"); - String tableSelectSql; - if (DatabaseConfigLoader.isH2(databaseName)) { - tableSelectSql = renderSelectSqlForH2(table); - } else { - tableSelectSql = renderSelectSql(table); + String tableReplacement = ""; + if (useTempTables) { // 构建临时表 + String tempTableName = "_temp_" + tableName.replace("$", ""); + + if (!createdTempTables.contains(tempTableName)) { + execRawSql(String.format("drop temporary table if exists %s;", tempTableName), databaseName); + + // 分批导入数据 + List dataLists = table.split(10000); + boolean tableCreated = false; + for (DataList dataList : dataLists) { + String dataInsertSql = tableCreated ? "insert into " + tempTableName + " " : "create temporary table " + tempTableName + " as "; + dataInsertSql += renderSelectSql(dataList, databaseName); + execRawSql(dataInsertSql, databaseName); + tableCreated = true; + } + } + + tableReplacement = tempTableName + " "; + } else { // 构建数据集sql语句 + StringBuilder builder = new StringBuilder(); + builder.append("(\n"); + String tableSelectSql = renderSelectSql(table, databaseName); + builder.append(tableSelectSql); + builder.append(") "); + tableReplacement = builder.toString(); } - builder.append(tableSelectSql); - builder.append(") "); // 替换到原sql里 - sqlLine = sqlLine.replace("$" + tableName + " ", builder.toString()); + sqlLine = sqlLine.replace("$" + tableName + " ", tableReplacement); } sqlBuilder.append(sqlLine); @@ -166,6 +184,14 @@ static String renderSql(String sql, Map tables, String databas return sqlBuilder.toString(); } + static String renderSelectSql(DataList table, String databaseName) { + if (DatabaseConfigLoader.isH2(databaseName)) { + return renderSelectSqlForH2(table); + } else { + return renderSelectSql(table); + } + } + static String renderSelectSql(DataList table) { if (table.rows.size() <= 0) { return renderEmptySelectSql(table); diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java index d3376e5..f8aa2c7 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java @@ -56,7 +56,7 @@ static void internalExec(String filePath, Consumer logPrinter) { if (sql.type == SqlType.SQL) { logPrinter.accept("SQL: " + sql.databaseName); logPrinter.accept(sql.sql); - dataList = SqlExecutor.exec(sql.sql, sql.databaseName, tables); + dataList = SqlExecutor.exec(sql.sql, sql.databaseName, tables, sql.useTempTables); lastResultName = sql.resultName != null ? sql.resultName : defaultResultName; tables.put(lastResultName, dataList); printSqlStatus(lastResultName, dataList, logPrinter, startTime); diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java index dfbc7d4..9cf7698 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java @@ -13,6 +13,7 @@ public static List loadSql(String filePath) { String fileContent = FileHelper.readFile(filePath); List sqlList = new ArrayList<>(); boolean exportNulls = false; + boolean useTempTables = false; for (String line : fileContent.split("\n")) { if (line.startsWith("# ")) { @@ -35,6 +36,16 @@ public static List loadSql(String filePath) { continue; } + if (line.startsWith("# use temp tables")) { + useTempTables = true; + continue; + } + + if (line.startsWith("# no use temp tables")) { + useTempTables = false; + continue; + } + // 导入 if (line.startsWith("# import ")) { Sql importSql = new Sql(); @@ -71,6 +82,7 @@ public static List loadSql(String filePath) { sql.databaseName = databaseName; sql.sql = ""; sql.resultName = getResultName(line); + sql.useTempTables = useTempTables; sqlList.add(sql); continue; } diff --git a/src/main/java/com/zwc/sqldataprocessor/entity/DataList.java b/src/main/java/com/zwc/sqldataprocessor/entity/DataList.java index f5ba1bf..3485784 100644 --- a/src/main/java/com/zwc/sqldataprocessor/entity/DataList.java +++ b/src/main/java/com/zwc/sqldataprocessor/entity/DataList.java @@ -1,8 +1,12 @@ package com.zwc.sqldataprocessor.entity; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.apache.commons.collections4.ListUtils; +import org.apache.poi.ss.formula.functions.T; + public class DataList { public List columns; @@ -14,4 +18,21 @@ public static enum ColumnType { TEXT } public List> rows = new ArrayList<>(); + + public List split(int maxRowCount) { + if (rows.size() <= maxRowCount) { + return Collections.singletonList(this); + } + + List>> partitions = ListUtils.partition(rows, maxRowCount); + List resultList = new ArrayList<>(); + for (List> partition : partitions) { + DataList dataList = new DataList(); + dataList.columns = columns; + dataList.columnTypes = columnTypes; + dataList.rows = partition; + resultList.add(dataList); + } + return resultList; + } } diff --git a/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java b/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java index 77bcd89..f34a229 100644 --- a/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java +++ b/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java @@ -8,6 +8,7 @@ public class Sql { public String sheetName; public String resultName; public boolean exportNulls; + public boolean useTempTables; public enum SqlType { SQL, IMPORT, From 201cbacb8f5138eb549af7d5b3091fa205880bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 09:32:30 +0800 Subject: [PATCH 2/8] refine --- src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java | 2 ++ src/main/java/com/zwc/sqldataprocessor/SqlLoader.java | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java index 75440b0..ef62a91 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java @@ -161,6 +161,8 @@ static String renderSql(String sql, Map tables, String databas execRawSql(dataInsertSql, databaseName); tableCreated = true; } + + createdTempTables.add(tempTableName); } tableReplacement = tempTableName + " "; diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java index 9cf7698..8b30f62 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java @@ -26,22 +26,22 @@ public static List loadSql(String filePath) { continue; } - if (line.startsWith("# no export nulls")) { + if (line.startsWith("# -exportNulls")) { exportNulls = false; continue; } - if (line.startsWith("# export nulls")) { + if (line.startsWith("# +exportNulls")) { exportNulls = true; continue; } - if (line.startsWith("# use temp tables")) { + if (line.startsWith("# +tempTables")) { useTempTables = true; continue; } - if (line.startsWith("# no use temp tables")) { + if (line.startsWith("# -tempTables")) { useTempTables = false; continue; } From eb77a1da0751bb9fe959695c9ee641b2def37988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 10:01:06 +0800 Subject: [PATCH 3/8] support temp tables --- .../com/zwc/sqldataprocessor/SqlExecutor.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java index ef62a91..21162bb 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java @@ -151,15 +151,15 @@ static String renderSql(String sql, Map tables, String databas if (!createdTempTables.contains(tempTableName)) { execRawSql(String.format("drop temporary table if exists %s;", tempTableName), databaseName); + String createTempTableSql = renderCreateTempTableSql(table, tempTableName); + execRawSql(createTempTableSql, databaseName); // 分批导入数据 List dataLists = table.split(10000); - boolean tableCreated = false; for (DataList dataList : dataLists) { - String dataInsertSql = tableCreated ? "insert into " + tempTableName + " " : "create temporary table " + tempTableName + " as "; + String dataInsertSql = "insert into " + tempTableName + " "; dataInsertSql += renderSelectSql(dataList, databaseName); execRawSql(dataInsertSql, databaseName); - tableCreated = true; } createdTempTables.add(tempTableName); @@ -186,6 +186,31 @@ static String renderSql(String sql, Map tables, String databas return sqlBuilder.toString(); } + static String renderCreateTempTableSql(DataList table, String tableName) { + StringBuilder builder = new StringBuilder(); + builder.append("create temporary table " + tableName + "("); + for (int index = 0; index < table.columns.size(); ++index) { + builder.append("`" + table.columns.get(index) + "` "); + + ColumnType columnType = table.columnTypes.get(index); + if (columnType == ColumnType.INT) { + builder.append("bigint"); + } else if (columnType == ColumnType.DECIMAL) { + builder.append("decimal"); + } else if (columnType == ColumnType.DATETIME) { + builder.append("datetime"); + } else { + builder.append("LONGTEXT"); + } + + if (index != table.columns.size() - 1) { + builder.append(","); + } + } + builder.append(") collate utf8mb4_general_ci CHARACTER SET utf8mb4;"); + return builder.toString(); + } + static String renderSelectSql(DataList table, String databaseName) { if (DatabaseConfigLoader.isH2(databaseName)) { return renderSelectSqlForH2(table); From d978bf3af7fc56e5c2a9eec54ac14a062e01b754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 23:08:54 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E6=94=AF=E6=8C=81xlsx=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 13 +-- .../zwc/sqldataprocessor/ExportExecutor.java | 10 +- .../zwc/sqldataprocessor/SqlFileExecutor.java | 6 +- .../com/zwc/sqldataprocessor/SqlLoader.java | 13 +++ .../com/zwc/sqldataprocessor/entity/Sql.java | 1 + .../exporter/CsvExporter.java | 7 +- .../sqldataprocessor/exporter/Exporter.java | 8 ++ .../exporter/XlsxExporter.java | 110 ++++++++++++++++++ .../importer/XlsImporter.java | 9 +- 9 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/zwc/sqldataprocessor/exporter/Exporter.java create mode 100644 src/main/java/com/zwc/sqldataprocessor/exporter/XlsxExporter.java diff --git a/pom.xml b/pom.xml index 0e11108..2b2b192 100644 --- a/pom.xml +++ b/pom.xml @@ -80,12 +80,6 @@ 4.4 - - org.apache.poi - poi - 3.10-FINAL - - com.h2database h2 @@ -95,13 +89,12 @@ org.apache.poi poi-ooxml - 3.10-FINAL + 5.2.2 - org.apache.poi - poi-ooxml-schemas - 3.10-FINAL + poi + 5.2.2 diff --git a/src/main/java/com/zwc/sqldataprocessor/ExportExecutor.java b/src/main/java/com/zwc/sqldataprocessor/ExportExecutor.java index d3e4841..2961fed 100644 --- a/src/main/java/com/zwc/sqldataprocessor/ExportExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/ExportExecutor.java @@ -2,21 +2,23 @@ import com.zwc.sqldataprocessor.entity.DataList; import com.zwc.sqldataprocessor.exporter.CsvExporter; +import com.zwc.sqldataprocessor.exporter.Exporter; +import com.zwc.sqldataprocessor.exporter.XlsxExporter; public class ExportExecutor { - public static String export(String resultName, DataList table, String path, boolean exportNulls) { + public static String export(String resultName, DataList table, String path, boolean exportNulls, boolean exportXlsx) { // 执行导出 - CsvExporter exporter = new CsvExporter(); + Exporter exporter = exportXlsx ? new XlsxExporter() : new CsvExporter(); byte[] bytes = exporter.export(table, exportNulls); // 写入导出文件 if (path == null) { - path = String.format("./%s.csv", resultName); + path = String.format("./%s.%s", resultName, exporter.getExtension()); path = FileHelper.writeOutputFile(path, bytes); return path; } else if (!path.contains("/") && !path.contains("\\")) { - path = String.format("./%s.csv", path); + path = String.format("./%s.%s", path, exporter.getExtension()); path = FileHelper.writeOutputFile(path, bytes); return path; } diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java index f8aa2c7..d33e78d 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlFileExecutor.java @@ -64,7 +64,7 @@ static void internalExec(String filePath, Consumer logPrinter) { // 导出 if (sql.type == SqlType.EXPORT || sql.type == SqlType.END) { - doExport(lastResultName, dataList, logPrinter, sql.fileName, sql.exportNulls); + doExport(lastResultName, dataList, logPrinter, sql.fileName, sql.exportNulls, sql.exportXlsx); // 提前结束 if (sql.type == SqlType.END) { @@ -75,10 +75,10 @@ static void internalExec(String filePath, Consumer logPrinter) { } } - static void doExport(String resultName, DataList dataList, Consumer logPrinter, String filePath, boolean exportNulls) { + static void doExport(String resultName, DataList dataList, Consumer logPrinter, String filePath, boolean exportNulls, boolean exportXlsx) { // 导出 logPrinter.accept("导出结果集: " + resultName); - String exportPath = ExportExecutor.export(resultName, dataList, filePath, exportNulls); + String exportPath = ExportExecutor.export(resultName, dataList, filePath, exportNulls, exportXlsx); exportPath = Paths.get(exportPath).toFile().getAbsolutePath(); logPrinter.accept("导出文件路径: " + exportPath); diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java index 8b30f62..e62755f 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java @@ -13,6 +13,7 @@ public static List loadSql(String filePath) { String fileContent = FileHelper.readFile(filePath); List sqlList = new ArrayList<>(); boolean exportNulls = false; + boolean exportXlsx = false; boolean useTempTables = false; for (String line : fileContent.split("\n")) { @@ -36,6 +37,16 @@ public static List loadSql(String filePath) { continue; } + if (line.startsWith("# +exportXlsx")) { + exportXlsx = true; + continue; + } + + if (line.startsWith("# -exportXlsx")) { + exportXlsx = false; + continue; + } + if (line.startsWith("# +tempTables")) { useTempTables = true; continue; @@ -64,6 +75,7 @@ public static List loadSql(String filePath) { Sql exportSql = new Sql(); exportSql.type = SqlType.EXPORT; exportSql.exportNulls = exportNulls; + exportSql.exportXlsx = exportXlsx; String exportFilePath = line.replace("# export", "").trim(); if (!exportFilePath.equals("")) { exportSql.fileName = exportFilePath; @@ -104,6 +116,7 @@ public static List loadSql(String filePath) { Sql exportSql = new Sql(); exportSql.type = SqlType.EXPORT; exportSql.exportNulls = exportNulls; + exportSql.exportXlsx = exportXlsx; sqlList.add(exportSql); } diff --git a/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java b/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java index f34a229..ae78f04 100644 --- a/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java +++ b/src/main/java/com/zwc/sqldataprocessor/entity/Sql.java @@ -8,6 +8,7 @@ public class Sql { public String sheetName; public String resultName; public boolean exportNulls; + public boolean exportXlsx; public boolean useTempTables; public enum SqlType { SQL, diff --git a/src/main/java/com/zwc/sqldataprocessor/exporter/CsvExporter.java b/src/main/java/com/zwc/sqldataprocessor/exporter/CsvExporter.java index 7332de6..6e7845e 100644 --- a/src/main/java/com/zwc/sqldataprocessor/exporter/CsvExporter.java +++ b/src/main/java/com/zwc/sqldataprocessor/exporter/CsvExporter.java @@ -8,7 +8,7 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; -public class CsvExporter { +public class CsvExporter implements Exporter { public byte[] export(DataList table, boolean exportNulls) { StringBuilder stringBuilder = new StringBuilder(); @@ -31,4 +31,9 @@ public byte[] export(DataList table, boolean exportNulls) { return stringBuilder.toString().getBytes(); } + @Override + public String getExtension() { + return "csv"; + } + } diff --git a/src/main/java/com/zwc/sqldataprocessor/exporter/Exporter.java b/src/main/java/com/zwc/sqldataprocessor/exporter/Exporter.java new file mode 100644 index 0000000..7146c49 --- /dev/null +++ b/src/main/java/com/zwc/sqldataprocessor/exporter/Exporter.java @@ -0,0 +1,8 @@ +package com.zwc.sqldataprocessor.exporter; + +import com.zwc.sqldataprocessor.entity.DataList; + +public interface Exporter { + byte[] export(DataList table, boolean exportNulls); + String getExtension(); +} diff --git a/src/main/java/com/zwc/sqldataprocessor/exporter/XlsxExporter.java b/src/main/java/com/zwc/sqldataprocessor/exporter/XlsxExporter.java new file mode 100644 index 0000000..e90a2ab --- /dev/null +++ b/src/main/java/com/zwc/sqldataprocessor/exporter/XlsxExporter.java @@ -0,0 +1,110 @@ +package com.zwc.sqldataprocessor.exporter; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +import com.zwc.sqldataprocessor.entity.DataList; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFFont; + +public class XlsxExporter implements Exporter { + + @Override + public byte[] export(DataList table, boolean exportNulls) { + LinkedHashMap tables = new LinkedHashMap<>(); + tables.put("sheet1", table); + return export(tables); + } + + @Override + public String getExtension() { + return "xlsx"; + } + + public byte[] export(LinkedHashMap tables) { + try (SXSSFWorkbook workbook = new SXSSFWorkbook(1000)) { + for (String sheetName : tables.keySet()) { + DataList table = tables.get(sheetName); + if (table.columns == null) { + continue; + } + SXSSFSheet sheet = workbook.createSheet(sheetName); + + // 写入表头 + XSSFCellStyle headStyle = (XSSFCellStyle) workbook.createCellStyle(); + headStyle.setAlignment(HorizontalAlignment.CENTER); + XSSFFont headFont = (XSSFFont )workbook.createFont(); + headFont.setBold(true); + headStyle.setFont(headFont); + int index = 0; + writeRow(sheet, index, table.columns, headStyle); + + // 写入行数据 + for (List row : table.rows) { + index++; + writeRow(sheet, index, row, null); + } + + // 检测列最大文字数 + int[] charCounts = new int[table.columns.size()]; + Consumer> detectCharCount = row -> { + for (int columnIndex = 0; columnIndex < row.size(); ++columnIndex) { + int charCount = row.get(columnIndex).getBytes(Charset.forName("GBK")).length; + charCounts[columnIndex] = Math.max(charCounts[columnIndex], charCount); + } + }; + detectCharCount.accept(table.columns); + for (List row : table.rows) { + detectCharCount.accept(row); + } + + // 设置列宽度 + for (int columnIndex = 0; columnIndex < table.columns.size(); ++columnIndex) { + // 限定宽度范围 + int charCount = Math.max(charCounts[columnIndex], 4); + charCount = Math.min(charCount, 80); + int width = (charCount + 4) * 220; + width = Math.min(width, 255*256); + sheet.setColumnWidth(columnIndex, width); + } + } + + // 生成二进制 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + workbook.write(outputStream); + return outputStream.toByteArray(); + + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * 写入行数据 + */ + void writeRow(SXSSFSheet sheet, int rowIndex, List values, CellStyle style) { + SXSSFRow row = sheet.createRow(rowIndex); + row.setHeight((short)400); + for (int columnIndex = 0; columnIndex < values.size(); ++columnIndex) { + SXSSFCell cell = row.createCell(columnIndex); + + // 写入数据 + cell.setCellValue(values.get(columnIndex)); + + // 设置样式 + if (style != null) { + cell.setCellStyle(style); + } + } + } +} diff --git a/src/main/java/com/zwc/sqldataprocessor/importer/XlsImporter.java b/src/main/java/com/zwc/sqldataprocessor/importer/XlsImporter.java index 32031f8..a91f3ab 100644 --- a/src/main/java/com/zwc/sqldataprocessor/importer/XlsImporter.java +++ b/src/main/java/com/zwc/sqldataprocessor/importer/XlsImporter.java @@ -13,8 +13,9 @@ import com.zwc.sqldataprocessor.entity.DataList; import com.zwc.sqldataprocessor.entity.DataList.ColumnType; import org.apache.commons.lang3.StringUtils; -import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -37,7 +38,7 @@ public DataList doImport(byte[] content, String sheetName) { if (isXlsx) { book = new XSSFWorkbook(new ByteArrayInputStream(content)); } else { - book = WorkbookFactory.create(new NPOIFSFileSystem(new ByteArrayInputStream(content))); + book = WorkbookFactory.create(new POIFSFileSystem(new ByteArrayInputStream(content))); } } catch (IOException e) { throw new RuntimeException(e); @@ -79,7 +80,7 @@ public DataList doImport(byte[] content, String sheetName) { } String value = cell.toString(); - if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) { + if (cell.getCellType() == CellType.NUMERIC) { if (value.contains("E")) { double doubleValue = Double.parseDouble(value); NumberFormat numberFormat = NumberFormat.getInstance(); @@ -88,7 +89,7 @@ public DataList doImport(byte[] content, String sheetName) { } else if (value.endsWith(".0")) { value = value.substring(0, value.lastIndexOf(".")); } - } else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) { + } else if (cell.getCellType() == CellType.FORMULA) { DecimalFormat decimalFormat = new DecimalFormat("0"); try { value = decimalFormat.format(cell.getNumericCellValue()); From 228dbaff0a46deb60fbac703f513b2bcc274440f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 23:28:17 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E6=94=AF=E6=8C=81h2=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E7=9A=84=E4=B8=B4=E6=97=B6=E8=A1=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- note.txt | 8 +++++++- .../com/zwc/sqldataprocessor/SqlExecutor.java | 15 +++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/note.txt b/note.txt index e3b28d4..2754aa8 100644 --- a/note.txt +++ b/note.txt @@ -1,4 +1,10 @@ 待开发功能: 针对常见的错误, 直接在控制台显示具体的错误, 不需要打印调用栈 支持临时表的模式, 防止临时表占用超出string大小. - 支持导出格式: xlsx, csv. \ No newline at end of file + 支持导出格式: xlsx, csv. + 更新readme文件 + 运行输出: 控制台+文件输出. + +代码重构: + Sql, 根据不同的语句类型, 拆分成不同的子类. + 使用实例化的方式来调用工具类, 防止方法参数过多的问题. \ No newline at end of file diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java index 21162bb..ddd9bb1 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlExecutor.java @@ -150,8 +150,12 @@ static String renderSql(String sql, Map tables, String databas String tempTableName = "_temp_" + tableName.replace("$", ""); if (!createdTempTables.contains(tempTableName)) { - execRawSql(String.format("drop temporary table if exists %s;", tempTableName), databaseName); - String createTempTableSql = renderCreateTempTableSql(table, tempTableName); + String sqlFormat = "drop temporary table if exists %s;"; + if (DatabaseConfigLoader.isH2(databaseName)) { + sqlFormat = "drop table if exists %s;"; + } + execRawSql(String.format(sqlFormat, tempTableName), databaseName); + String createTempTableSql = renderCreateTempTableSql(table, tempTableName, databaseName); execRawSql(createTempTableSql, databaseName); // 分批导入数据 @@ -186,7 +190,7 @@ static String renderSql(String sql, Map tables, String databas return sqlBuilder.toString(); } - static String renderCreateTempTableSql(DataList table, String tableName) { + static String renderCreateTempTableSql(DataList table, String tableName, String databaseName) { StringBuilder builder = new StringBuilder(); builder.append("create temporary table " + tableName + "("); for (int index = 0; index < table.columns.size(); ++index) { @@ -207,7 +211,10 @@ static String renderCreateTempTableSql(DataList table, String tableName) { builder.append(","); } } - builder.append(") collate utf8mb4_general_ci CHARACTER SET utf8mb4;"); + builder.append(") "); + if (DatabaseConfigLoader.isMySql(databaseName)) { + builder.append("collate utf8mb4_general_ci CHARACTER SET utf8mb4"); + } return builder.toString(); } From fc553c4f553338912b7a8a172058c5670408231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Wed, 29 Nov 2023 23:40:19 +0800 Subject: [PATCH 6/8] refine --- .../com/zwc/sqldataprocessor/SqlLoader.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java index e62755f..9aecbdc 100644 --- a/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java +++ b/src/main/java/com/zwc/sqldataprocessor/SqlLoader.java @@ -19,40 +19,41 @@ public static List loadSql(String filePath) { if (line.startsWith("# ")) { line = line.trim(); + String lowerLine = line.toLowerCase(); - if (line.startsWith("# end")) { + if (lowerLine.equals("# end")) { Sql endSql = new Sql(); endSql.type = SqlType.END; sqlList.add(endSql); continue; } - if (line.startsWith("# -exportNulls")) { - exportNulls = false; + if (lowerLine.equals("# exportnulls")) { + exportNulls = true; continue; } - if (line.startsWith("# +exportNulls")) { - exportNulls = true; + if (lowerLine.equals("# -exportnulls")) { + exportNulls = false; continue; } - if (line.startsWith("# +exportXlsx")) { + if (lowerLine.equals("# exportxlsx")) { exportXlsx = true; continue; } - if (line.startsWith("# -exportXlsx")) { + if (lowerLine.equals("# -exportxlsx")) { exportXlsx = false; continue; } - if (line.startsWith("# +tempTables")) { + if (lowerLine.equals("# temptables")) { useTempTables = true; continue; } - if (line.startsWith("# -tempTables")) { + if (lowerLine.equals("# -temptables")) { useTempTables = false; continue; } From 735cb43dfff6c882f300cef20a547a51918b8096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Thu, 30 Nov 2023 09:45:32 +0800 Subject: [PATCH 7/8] refine --- README.md | 22 +++++++++++++++++----- note.txt | 5 +---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 16265e5..85d4de2 100644 --- a/README.md +++ b/README.md @@ -62,11 +62,11 @@ select 1 as a; # 数据库配置文件 -在当前目录下, 文件名称为: `databases.json`. 首次运行时会自动生成这个文件的默认内容, 默认自带一个 [h2](http://www.h2database.com/html/commands.html) 内存数据库, 名称为 `h2`, 可以做简单的测试和使用. +在当前目录下, 文件名称为: `databases.json`. 首次运行时会自动生成这个文件的默认内容, 默认自带一个 [h2](http://www.h2database.com/html/commands.html) 内存数据库, 名称为 `h2`, 可以做简单的测试和使用. 推荐自己配置一个mysql数据库使用. # SQL文件结构和语法定义 -文件后缀建议为sql, 以让文本编辑器支持sql语法的高亮显示. +文件后缀建议为sql, 以让你的文本编辑器支持sql语法的高亮显示. ## `# import` 导入一个xls, xlsx或csv文件 @@ -115,7 +115,7 @@ from test_table temp; ## 在SQL中可以使用 `$xxx` 的方式来引用之前的结果集 -注意: 一定要在结果集后面加一个自己的自定义名称, 结果集是以一个sub query的方式运行的, sql标准要求一定要指定一个别名的. +注意: 一定要在结果集后面加一个自己的自定义名称, 结果集是以一个子查询(或临时表)的方式运行的, sql标准要求子查询一定要指定一个别名. ```sql # import /Users/xxx/Downloads/xxx.xlsx @@ -189,6 +189,18 @@ from test_table2 ; ``` -## 关于空值null的导出显示控制 +## 空值null的导出显示控制 -默认情况下, 值为null会在导出时候显示成空字符串. 可以使用 `# export nulls` 来指定在导出文件的时候, null被导出为``, 这样可以看出来具体哪个是null值, 区分与空字符串. 然后, 后续可以使用 `# no export nulls` 来恢复默认行为, 即null被导出为空白, 和空字符串显示一样. \ No newline at end of file +默认情况下, 值为null会在导出时候显示成空字符串. 可以使用`# exportnulls`来指定在导出文件的时候, null被导出为``, 这样可以看出来具体哪个是null值, 与空字符串作区分. 可以使用`# -exportnulls`来恢复默认行为, 即null被导出为空白, 和空字符串显示一样. + +## 导出xlsx格式 + +使用`# exportxlsx`设置xlsx导出格式, 使用`# -exportxlsx`关闭xlsx导出格式. 默认: 关闭xlsx导出格式, 即导出格式为csv. + +## 临时表模式 + +默认情况下, 如果一个sql里引用了结果集, 结果集会以子查询的方式嵌入到sql中. 如果结果集数据量很大, 会导致子查询的sql很大, 从而导致超出java里String的最大容量, 或者超出mysql库的最大sql长度. + +使用临时表模式, 可以预先把结果集的数据导入到临时表中, 然后sql中直接从临时表查询结果集, 这样sql就比较短. + +使用`# temptables`打开临时表模式, 使用`# -temptables`关闭临时表模式. 默认: 关闭临时表模式. \ No newline at end of file diff --git a/note.txt b/note.txt index 2754aa8..e7b62de 100644 --- a/note.txt +++ b/note.txt @@ -1,10 +1,7 @@ 待开发功能: 针对常见的错误, 直接在控制台显示具体的错误, 不需要打印调用栈 - 支持临时表的模式, 防止临时表占用超出string大小. - 支持导出格式: xlsx, csv. - 更新readme文件 运行输出: 控制台+文件输出. 代码重构: Sql, 根据不同的语句类型, 拆分成不同的子类. - 使用实例化的方式来调用工具类, 防止方法参数过多的问题. \ No newline at end of file + 使用实例化的方式来调用工具类, 防止方法参数过多的问题. From 79a56222771927fdad891b420aafc2eb946a2df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E6=96=87=E8=B6=85=20Reid=20Zeng?= Date: Thu, 30 Nov 2023 09:49:23 +0800 Subject: [PATCH 8/8] refine --- note.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/note.txt b/note.txt index e7b62de..e0afaca 100644 --- a/note.txt +++ b/note.txt @@ -1,6 +1,7 @@ 待开发功能: 针对常见的错误, 直接在控制台显示具体的错误, 不需要打印调用栈 运行输出: 控制台+文件输出. + 子查询模式下, 检查子查询的长度(只有第一个select才需要指定列名称, 后续select不需要执行列名称) 代码重构: Sql, 根据不同的语句类型, 拆分成不同的子类.