
12-DatabaseType探测与DdlExecutor自动建表部署到任何空库上启动即建表、自动识别数据库、选对方言——这是EA引擎开箱即用的两块基石。这篇拆90行的DatabaseType探测逻辑包括GBase靠端口号区分的冷知识和6.4KB的DdlExecutor。文章目录12-DatabaseType探测与DdlExecutor自动建表一、DatabaseType枚举即工厂二、detect()两个信号的交叉判断三、createDialect()一个switch四、DdlExecutor启动链的第三、四步4.1 executeAutoCreate的流程4.2 executeAutoUpgrade加列不破坏存量五、这套机制换来的部署体验六、自动DDL的边界一、DatabaseType枚举即工厂browise-metadata/src/main/java/com/browise/ea/core/ddl/DatabaseType.java90行。三个职责拧在一个枚举里类型常量 探测器 方言工厂。publicenumDatabaseType{ORACLE,MSSQL,MYSQL,GBASE8S,GBASE8A,DM,KINGBASE_ORACLE,KINGBASE_PG;publicstaticDatabaseTypedetect(Connectionconn)throwsSQLException{...}publicstaticDatabaseTypedetect(Connectionconn,booleankingbasePgMode){...}publicDialectcreateDialect(){...}}为什么用枚举不用类——类型集合是封闭的8种不打算让用户自定义数据库枚举天然封闭、单例、可switch——比工厂类注册表简洁。二、detect()两个信号的交叉判断publicstaticDatabaseTypedetect(Connectionconn)throwsSQLException{Stringurlconn.getMetaData().getURL().toLowerCase();StringproductNameconn.getMetaData().getDatabaseProductName().toUpperCase();if(url.contains(oracle)||productName.contains(ORACLE))returnORACLE;if(url.contains(sqlserver)||productName.contains(SQL SERVER))returnMSSQL;if(url.contains(mysql)||productName.contains(MYSQL))returnMYSQL;// GBase8S 通过端口号 9088 区分if(productName.contains(GBASE)url.contains(9088))returnGBASE8S;if(productName.contains(GBASE))returnGBASE8A;if(productName.contains(DM)||productName.contains(DM DBMS))returnDM;if(productName.contains(KINGBASE)||productName.contains(KingbaseES))returnKINGBASE_ORACLE;thrownewRuntimeException(不支持的数据库: productName);}两个信号源JDBC URL驱动连接串和 DatabaseProductName驱动自报家门。任一命中即判定。三处细节值得注意GBase8S靠端口号9088区分——8a分析型和8s事务型的productName都含GBASEURL格式也几乎一样。唯一的区分信号是默认端口9088。这是国产数据库生态的现实——元数据不规范只能靠约定俗成的端口。如果用户改了端口探测错就只能靠配置硬指定。达梦的判断productName.contains(DM)——DM这个两个字母的子串可能误判任何产品名含DM的字符串。实际达梦返回的productName是DM DBMScontains(DM)先命中也不会走到更精确的分支——判断顺序在这里就是优先级短前缀放在后面有风险但当前顺序里DM前面只有Oracle/MySQL/SQLServer/GBASE误判面可控。KingBase默认判为Oracle模式——detect(conn)不知道用户装的什么兼容模式保守默认Oracle兼容ROWNUM那套。PG模式靠第二个重载publicstaticDatabaseTypedetect(Connectionconn,booleankingbasePgMode){DatabaseTypetypedetect(conn);if(typeKINGBASE_ORACLEkingbasePgMode){returnKINGBASE_PG;// 配置项 browise.metadata.kingbase-pg-modetrue 切换}returntype;}探测不出来的信息必须显式配置——兼容模式是安装时的选择不写在元数据里引擎无从探测。这是自动探测的边界能探测的自动探测不了的留配置口。三、createDialect()一个switchpublicDialectcreateDialect(){switch(this){caseORACLE:returnnewOracleDialect();caseMSSQL:returnnewMssqlDialect();caseMYSQL:returnnewMySqlDialect();caseDM:returnnewDmDialect();caseKINGBASE_ORACLE:returnnewKingBaseOracleDialect();caseKINGBASE_PG:returnnewKingBasePgDialect();caseGBASE8S:returnnewGBase8sDialect();caseGBASE8A:returnnewGBase8aDialect();default:returnnewOracleDialect();// 兜底Oracle}}每次调用new一个新实例——方言无状态全是字符串拼接方法不需要单例。default兜底Oracle是防御性写法新增枚举值忘了配方言时不会NPE按最成熟的Oracle行为跑跑错了至少有日志能查。四、DdlExecutor启动链的第三、四步core/ddl/DdlExecutor.java6.4KB两个public入口DdlExecutorddlExecutornewDdlExecutor();ddlExecutor.executeAutoCreate(provider,config);// 表不存在→建ddlExecutor.executeAutoUpgrade(provider,config);// 表存在但缺列→补4.1 executeAutoCreate的流程对 EA01/EA02/EA03/EA05 每张表: ① tableExists(conn, EA01) → 按方言查系统表Oracle: user_tables / MySQL: information_schema ② 不存在 → 取方言的建表DDL模板 → 逐条执行 列类型由 dialect.columnType(dataTypeCode) 生成 Oracle: VARCHAR2(512) / DATE / NUMBER(18,2) MySQL: varchar(512) / datetime / decimal(18,2) ③ 执行 dialect.commentTable / commentColumn 加注释 Oracle: COMMENT ON TABLE EA01 IS SQL定义表建表DDL也是方言生成的——8个方言各自带EA五表的建表模板。加新方言时15个Dialect方法之外还要带DDL模板——这是第09篇说15个方法之外的隐藏工作量。4.2 executeAutoUpgrade加列不破坏存量版本迭代给EA03加了新列比如maskRule——已部署的库里EA03存在但没有这列。AutoUpgrade的处理对每张元数据表: ① 取方言的完整列清单 ② 逐列检查存在性查user_tab_columns ③ 缺的列 → ALTER TABLE ADD COLUMN类型由columnType生成只加列不删列——删列会毁数据。多余的旧列留在表里无害MetadataLoader按列名SELECT。五、这套机制换来的部署体验没有DdlExecutor之前的部署清单1. 找DBA要建表脚本哪个库的Oracle还是达梦 2. 手工执行13个脚本doc/目录 3. 顺序错了外键报错 4. 版本升级再手工对比加列有了之后1. 改db.properties的JDBC URL 2. 启动 → DdlExecutor检测五表不存在 → 按当前库方言建好注释 → DatabaseType.detect识别库类型 → 注入正确方言 → MetadataLoader装载此时表刚建好空的→ 管理界面配元数据demo应用的BrowiseIntegrationTest验证了这条链——测试连一个空Oracle启动后断言EA01建好、插入元数据、查询走通。自动建表不是便利功能是被集成测试保护的行为契约。六、自动DDL的边界这套机制管的是引擎自己的五张元数据表不管业务表。业务表SYS_USER等还是走doc/目录的手工脚本。为什么不全自动——元数据表结构由引擎版本决定升级时AutoUpgrade知道该加什么列业务表结构由业务决定引擎不知道你的SYS_USER有没有PSN_AGE列。自动化的边界划在谁拥有这个结构的知识——引擎拥有元数据表所以自动业务拥有业务表所以手工。✅ 亮点90行枚举拆出探测双信号URLproductName、GBase靠端口9088区分的国产库现实、KingBase模式探测不了必须配置的边界、AutoCreate/AutoUpgrade只加列不删列的原则。适合做开箱即用中间件的人。扩展方向第13篇describe元数据输出、第09篇方言体系。