futuretask仅用于并发解析实体类并生成建表sql,不参与执行;建表执行需单线程或限流串行化,以避免连接池争抢与事务混乱。

在自研 ORM 框架中,FutureTask 本身不参与实体属性映射或建表逻辑,它只负责异步执行任务并封装结果。真正完成“自动化并发建表”的核心环节是:解析实体类(含属性→字段映射)、生成建表 SQL、提交数据库执行。FutureTask 的作用,是把多个建表动作从串行变成可调度、可等待的并发单元——关键在于“在哪里用”和“怎么包装”。
明确建表流程中的并发切入点
建表不是原子操作,而是分阶段的:
- 扫描所有实体类(如
Book.class、User.class) - 对每个实体,解析其注解(如
@ZxTable、@ZxColumn)、主键、索引、时间字段等元数据 - 根据元数据生成对应数据库的建表语句(如 MySQL 的
CREATE TABLE ...) - 执行 SQL(需获取连接、提交事务)
其中,第1–3步纯内存操作,无IO,适合并发;第4步涉及数据库连接池竞争,需控制并发度,不宜盲目多线程执行。
用 FutureTask 包装“解析+SQL生成”阶段
把每个实体的元数据解析与 SQL 构建封装为一个独立任务,避免阻塞主线程,也便于统一异常处理和结果收集:
- 定义任务逻辑(Lambda 或 Callable):
比如:`() -> sqlBuilder.buildCreateSql(Book.class)` - 每个任务返回生成的 SQL 字符串,或封装了 SQL + 表名 + 是否已存在等信息的 POJO
- 提交到线程池:
`FuturebookSqlTask = executor.submit(() -> builder.build(Book.class));` - 批量收集结果:
`ListallSqls = tasks.stream().map(Future::get).collect(Collectors.toList());`
建表执行阶段要慎用 FutureTask 直接发 SQL
直接让每个 FutureTask 去执行 connection.createStatement().execute(sql) 容易引发问题:
- 连接池资源争抢(尤其高并发下抛
CannotGetJdbcConnectionException) - 事务边界混乱(每个 FutureTask 默认无事务上下文)
- 错误难以归因(哪个表建失败?哪条 SQL 报错?)
更稳妥的做法是:
- 先并发生成所有 SQL(用 FutureTask)
- 再由单线程或受控线程数(如固定 2–4 线程)按顺序/分批执行建表 SQL
- 执行时显式管理连接与事务,例如:
`dbProxyCore.executeInTransaction(conn -> { conn.createStatement().execute(sql); });`
结合框架已有结构做轻量集成
以你知识库中提到的 Zhaoxi.DbProxy 框架为例,可在 DbProxyCore 初始化阶段插入此逻辑:
- 在
DbProxyCore.Init()中扫描Assembly.GetTypes().Where(t => t.IsClass && t.GetCustomAttribute<zxtableattribute>() != null)</zxtableattribute> - 为每个匹配类型创建
FutureTask<createtablespec></createtablespec>,内部调用SqlBuilderExtension.BuildCreateSqlForType(t) - 汇总所有
CreateTableSpec后,过滤掉已存在的表(查information_schema.tables),再批量执行 - 最终日志输出类似:
"✅ 自动创建 7 张表,耗时 842ms(并发解析 320ms + 串行建表 522ms)"











