From 104d4b5476edc2d601e4c3a5a5646b4453e8a421 Mon Sep 17 00:00:00 2001 From: carm Date: Wed, 17 Aug 2022 02:31:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(metadata):=20=E6=8F=90=E4=BE=9B=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E9=AB=98=E6=95=88=E7=9A=84metadata=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mention #59 , #60 --- .../cc/carm/lib/easysql/api/SQLManager.java | 68 +++++++++++++++---- .../api/builder/TableMetadataBuilder.java | 22 ++++++ .../impl/TableMetadataBuilderImpl.java | 44 ++++++++++++ .../lib/easysql/manager/SQLManagerImpl.java | 48 ++++++++++--- 4 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 api/src/main/java/cc/carm/lib/easysql/api/builder/TableMetadataBuilder.java create mode 100644 impl/src/main/java/cc/carm/lib/easysql/builder/impl/TableMetadataBuilderImpl.java diff --git a/api/src/main/java/cc/carm/lib/easysql/api/SQLManager.java b/api/src/main/java/cc/carm/lib/easysql/api/SQLManager.java index fa3a978..01240c5 100644 --- a/api/src/main/java/cc/carm/lib/easysql/api/SQLManager.java +++ b/api/src/main/java/cc/carm/lib/easysql/api/SQLManager.java @@ -7,18 +7,22 @@ import cc.carm.lib.easysql.api.action.SQLUpdateBatchAction; import cc.carm.lib.easysql.api.builder.*; import cc.carm.lib.easysql.api.function.SQLDebugHandler; import cc.carm.lib.easysql.api.function.SQLExceptionHandler; +import cc.carm.lib.easysql.api.function.SQLFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import javax.sql.DataSource; import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.Executors; import java.util.function.Supplier; /** @@ -35,19 +39,28 @@ public interface SQLManager { /** * 获取用于执行 {@link SQLAction#executeAsync()} 的线程池。 - *
默认线程池为 {@link ThreadPoolExecutor} ,大小为 3。 + *
默认线程池为 {@link #defaultExecutorPool(String)} 。 * * @return {@link ExecutorService} */ @NotNull ExecutorService getExecutorPool(); /** - * 设定用于执行 {@link SQLAction#executeAsync()} 的线程池。 + * 设定用于执行 {@link SQLAction#executeAsync()} 的线程池. + *
默认线程池为 {@link #defaultExecutorPool(String)} 。 * * @param executorPool {@link ExecutorService} */ void setExecutorPool(@NotNull ExecutorService executorPool); + static ExecutorService defaultExecutorPool(String threadName) { + return Executors.newFixedThreadPool(4, r -> { + Thread thread = new Thread(r, threadName); + thread.setDaemon(true); + return thread; + }); + } + /** * 设定是否启用调试模式。 @@ -172,7 +185,29 @@ public interface SQLManager { @Nullable List executeSQLBatch(@NotNull Iterable sqlBatch); /** - * 在库中创建一个表 + * 获取并操作 {@link DatabaseMetaData} 以得到需要的数据库消息。 + * + * @param metadata 操作与返回的方法 + * @param 最终结果的返回类型 + * @return 最终结果,通过 {@link CompletableFuture#get()} 可阻塞并等待结果返回。 + */ + CompletableFuture fetchMetadata(@NotNull SQLFunction metadata); + + /** + * 获取并操作 {@link DatabaseMetaData} 提供的指定 {@link ResultSet} 以得到需要的数据库消息。 + *
该方法会自动关闭 {@link ResultSet} 。 + * + * @param supplier 操作 {@link DatabaseMetaData} 以提供信息所在的 {@link ResultSet} + * @param reader 读取 {@link ResultSet} 中指定信息的方法 + * @param 最终结果的返回类型 + * @return 最终结果,通过 {@link CompletableFuture#get()} 可阻塞并等待结果返回。 + * @throws NullPointerException 当 supplier 提供的 {@link ResultSet} 为NULL时抛出 + */ + CompletableFuture fetchMetadata(@NotNull SQLFunction supplier, + @NotNull SQLFunction<@NotNull ResultSet, R> reader); + + /** + * 在库中创建一个表。 * * @param tableName 表名 * @return {@link TableCreateBuilder} @@ -180,7 +215,7 @@ public interface SQLManager { TableCreateBuilder createTable(@NotNull String tableName); /** - * 对库中的某个表执行更改 + * 对库中的某个表执行更改。 * * @param tableName 表名 * @return {@link TableAlterBuilder} @@ -188,14 +223,23 @@ public interface SQLManager { TableAlterBuilder alterTable(@NotNull String tableName); /** - * 新建一个查询 + * 快速获取表的部分元数据。 + *
当需要获取其他元数据时,请使用 {@link #fetchMetadata(SQLFunction, SQLFunction)} 方法。 + * + * @param tablePattern 表名通配符 + * @return {@link TableMetadataBuilder} + */ + TableMetadataBuilder fetchTableMetadata(@NotNull String tablePattern); + + /** + * 新建一个查询。 * * @return {@link QueryBuilder} */ QueryBuilder createQuery(); /** - * 创建一条插入操作 + * 创建一条插入操作。 * * @param tableName 目标表名 * @return {@link InsertBuilder} @@ -203,7 +247,7 @@ public interface SQLManager { InsertBuilder> createInsert(@NotNull String tableName); /** - * 创建支持多组数据的插入操作 + * 创建支持多组数据的插入操作。 * * @param tableName 目标表名 * @return {@link InsertBuilder} @@ -211,7 +255,7 @@ public interface SQLManager { InsertBuilder> createInsertBatch(@NotNull String tableName); /** - * 创建一条替换操作 + * 创建一条替换操作。 * * @param tableName 目标表名 * @return {@link ReplaceBuilder} @@ -219,7 +263,7 @@ public interface SQLManager { ReplaceBuilder> createReplace(@NotNull String tableName); /** - * 创建支持多组数据的替换操作 + * 创建支持多组数据的替换操作。 * * @param tableName 目标表名 * @return {@link ReplaceBuilder} @@ -227,7 +271,7 @@ public interface SQLManager { ReplaceBuilder> createReplaceBatch(@NotNull String tableName); /** - * 创建更新操作 + * 创建更新操作。 * * @param tableName 目标表名 * @return {@link UpdateBuilder} @@ -235,7 +279,7 @@ public interface SQLManager { UpdateBuilder createUpdate(@NotNull String tableName); /** - * 创建删除操作 + * 创建删除操作。 * * @param tableName 目标表名 * @return {@link DeleteBuilder} diff --git a/api/src/main/java/cc/carm/lib/easysql/api/builder/TableMetadataBuilder.java b/api/src/main/java/cc/carm/lib/easysql/api/builder/TableMetadataBuilder.java new file mode 100644 index 0000000..5b207d3 --- /dev/null +++ b/api/src/main/java/cc/carm/lib/easysql/api/builder/TableMetadataBuilder.java @@ -0,0 +1,22 @@ +package cc.carm.lib.easysql.api.builder; + +import cc.carm.lib.easysql.api.SQLBuilder; + +import java.util.concurrent.CompletableFuture; + +public interface TableMetadataBuilder extends SQLBuilder { + + /** + * @return 本表是否存在 + */ + CompletableFuture validateExist(); + + /** + * @param columnName 需要判断的列名 + * @return 对应列是否存在 + */ + CompletableFuture isColumnExists(String columnName); + + // More coming soon. + +} diff --git a/impl/src/main/java/cc/carm/lib/easysql/builder/impl/TableMetadataBuilderImpl.java b/impl/src/main/java/cc/carm/lib/easysql/builder/impl/TableMetadataBuilderImpl.java new file mode 100644 index 0000000..c2d9b2d --- /dev/null +++ b/impl/src/main/java/cc/carm/lib/easysql/builder/impl/TableMetadataBuilderImpl.java @@ -0,0 +1,44 @@ +package cc.carm.lib.easysql.builder.impl; + +import cc.carm.lib.easysql.api.builder.TableMetadataBuilder; +import cc.carm.lib.easysql.api.function.SQLFunction; +import cc.carm.lib.easysql.builder.AbstractSQLBuilder; +import cc.carm.lib.easysql.manager.SQLManagerImpl; +import org.jetbrains.annotations.NotNull; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.util.concurrent.CompletableFuture; + +public class TableMetadataBuilderImpl + extends AbstractSQLBuilder + implements TableMetadataBuilder { + + protected final @NotNull String tablePattern; + + public TableMetadataBuilderImpl(@NotNull SQLManagerImpl manager, @NotNull String tablePattern) { + super(manager); + this.tablePattern = tablePattern; + } + + @Override + public CompletableFuture validateExist() { + return validate((meta) -> meta.getTables(null, null, tablePattern, new String[]{"TABLE"})); + } + + @Override + public CompletableFuture isColumnExists(String columnPattern) { + return validate((meta) -> meta.getColumns(null, null, tablePattern, columnPattern)); + } + + /** + * fast validate EXISTS. + * + * @param supplier supplier to get result set + * @return result future + */ + private CompletableFuture validate(SQLFunction supplier) { + return getManager().fetchMetadata(supplier, ResultSet::next); + } + +} diff --git a/impl/src/main/java/cc/carm/lib/easysql/manager/SQLManagerImpl.java b/impl/src/main/java/cc/carm/lib/easysql/manager/SQLManagerImpl.java index 22f9f67..63cc07e 100644 --- a/impl/src/main/java/cc/carm/lib/easysql/manager/SQLManagerImpl.java +++ b/impl/src/main/java/cc/carm/lib/easysql/manager/SQLManagerImpl.java @@ -12,6 +12,7 @@ import cc.carm.lib.easysql.api.action.SQLUpdateBatchAction; import cc.carm.lib.easysql.api.builder.*; import cc.carm.lib.easysql.api.function.SQLDebugHandler; import cc.carm.lib.easysql.api.function.SQLExceptionHandler; +import cc.carm.lib.easysql.api.function.SQLFunction; import cc.carm.lib.easysql.builder.impl.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -20,11 +21,13 @@ import org.slf4j.LoggerFactory; import javax.sql.DataSource; import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Supplier; public class SQLManagerImpl implements SQLManager { @@ -50,12 +53,7 @@ public class SQLManagerImpl implements SQLManager { String managerName = "SQLManager" + (name != null ? "#" + name : ""); this.LOGGER = logger; this.dataSource = dataSource; - this.executorPool = Executors.newFixedThreadPool(3, r -> { - Thread thread = new Thread(r, managerName); - thread.setDaemon(true); - return thread; - }); - + this.executorPool = SQLManager.defaultExecutorPool(managerName); this.exceptionHandler = SQLExceptionHandler.detailed(getLogger()); this.debugHandler = SQLDebugHandler.defaultHandler(getLogger()); } @@ -80,10 +78,6 @@ public class SQLManagerImpl implements SQLManager { this.debugHandler = debugHandler; } - public void debug(String msg) { - if (isDebugMode()) getLogger().info("[DEBUG] " + msg); - } - @Override public Logger getLogger() { return LOGGER; @@ -160,6 +154,33 @@ public class SQLManagerImpl implements SQLManager { return action.execute(null); } + @Override + public CompletableFuture fetchMetadata(@NotNull SQLFunction metadata) { + return CompletableFuture.supplyAsync(() -> { + try (Connection conn = getConnection()) { + return metadata.apply(conn.getMetaData()); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + }, this.executorPool); + } + + @Override + public CompletableFuture fetchMetadata(@NotNull SQLFunction supplier, + @NotNull SQLFunction<@NotNull ResultSet, R> reader) { + return CompletableFuture.supplyAsync(() -> { + try ( + Connection conn = getConnection(); + ResultSet rs = supplier.apply(conn.getMetaData()) + ) { + if (rs == null) throw new NullPointerException("Metadata返回的ResultSet为null。"); + else return reader.apply(rs); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + }, this.executorPool); + } + @Override public TableCreateBuilder createTable(@NotNull String tableName) { return new TableCreateBuilderImpl(this, tableName); @@ -170,6 +191,11 @@ public class SQLManagerImpl implements SQLManager { return new TableAlterBuilderImpl(this, tableName); } + @Override + public TableMetadataBuilder fetchTableMetadata(@NotNull String tablePattern) { + return new TableMetadataBuilderImpl(this, tablePattern); + } + @Override public QueryBuilder createQuery() { return new QueryBuilderImpl(this);