JDBC元数据驱动的XML自动生成工具
简介:这是一款面向Java开发人员与数据库管理员的XML自动化生成工具,解决手动编写XML易出错、效率低的问题,特别适用于需频繁将数据库查询结果导出为结构化XML配置文件或数据交换文档的场景。资源包共4个文件,含1个可执行程序(JdbcGenerator.exe)用于驱动整个生成流程,1个INI配置文件(init.ini)管理基础参数,1个XML模板文件(global.datasource.xml)定义输出结构,以及1个properties文件(jdbc.properties)存储数据库连接信息,整体仅144KB,轻量易部署。已有2354人学习下载,说明其在中小型项目快速集成与教学演示中具备较高实用性。用户可直接运行exe程序,通过JDBC连接MySQL、Oracle等主流数据库,自动将SQL查询结果映射为符合规范的UTF-8编码XML文档,并支持自定义根节点、字段命名与嵌套关系,省去手写标签、校验格式与调试解析的繁琐环节。
1. XML 文件自动生成工具:不是写死模板的替换器,而是 JDBC 元数据驱动的动态结构生成器
你有没有遇到过这种场景:新接一个数据库表结构变更频繁的遗留系统,每次加字段就要手动改三份 XML——Spring 的datasource配置、MyBatis 的resultMap、还有某中间件要求的元数据描述文件?改漏一个,服务启动就报Invalid column name,查日志要翻半小时。更糟的是,当表从 5 张扩到 37 张,XML 文件数飙到 120+,连 diff 都不敢轻易 merge。这个「XML 文件自动生成工具」不是用字符串拼接糊出来的模板引擎,它本质是一个轻量级 JDBC 元数据反射器:通过真实连接数据库(支持 MySQL/PostgreSQL/Oracle),读取INFORMATION_SCHEMA或ALL_TAB_COLUMNS,把表名、字段名、类型、长度、是否为空、主键标识等结构信息,按预设规则映射成符合规范的 XML 片段。它不替代 IDE 的代码生成插件,也不做运行时动态解析;它的价值在于——一次配置,永久同步,且所有输出 XML 可直接被 Spring Boot 2.7+、MyBatis 3.4.6+、以及主流国产中间件识别。适合后端工程师、DBA 协同交付阶段、或需要快速搭建数据接入层的集成项目负责人。如果你还在用 Excel 手动维护字段映射表,那它就是你的后悔药。
2. 工具原理与核心能力:为什么必须基于 JDBC 而非 DDL 解析?
2.1 为什么不用正则解析 CREATE TABLE 语句?
很多团队第一反应是“写个脚本 parse DDL SQL”,但实操中血泪经验太多:
- Oracle 的
NUMBER(10,2)和 PostgreSQL 的NUMERIC(10,2)在 DDL 中写法一致,但 JDBCgetJDBCType()返回值不同(Types.DECIMALvsTypes.NUMERIC),影响后续 XML 中<type>字段的语义准确性; - MySQL 5.7 的
TINYINT(1)常被业务当布尔用,但 DDL 里看不出逻辑含义,而 JDBCgetColumnClassName()能返回"java.lang.Boolean"; - 某些国产数据库(如达梦)的
VARCHAR2类型在 DDL 中无长度声明,但实际建表时隐式限制为 2000,仅靠 DDL 无法获取该约束。
提示:本工具强制走 JDBC
DatabaseMetaData接口,因为它是 JDBC 规范定义的唯一跨厂商元数据访问标准。哪怕底层是闭源数据库,只要其 JDBC Driver 实现了getColumns()方法,就能拿到真实运行时结构。
2.2 核心流程:从 Connection 到 XML 的四步链路
整个生成过程严格遵循 JDBC 元数据调用链,不依赖任何外部 schema 文件或人工输入:
- 建立连接并获取元数据对象
使用标准DriverManager.getConnection(),传入 JDBC URL、用户名、密码;调用conn.getMetaData()获取DatabaseMetaData实例。 - 枚举目标表列表
调用metaData.getTables(null, schemaPattern, tableNamePattern, new String[]{"TABLE"}),支持通配符(如"user_%")和多 schema(如["public","app"])。 - 逐表获取字段详情
对每个表,调用metaData.getColumns(null, schema, table, "%"),返回ResultSet,每行含COLUMN_NAME,DATA_TYPE,COLUMN_SIZE,NULLABLE,COLUMN_DEF,IS_NULLABLE,IS_AUTOINCREMENT,IS_GENERATEDCOLUMN等 20+ 列。 - 字段到 XML 节点的映射规则
不是简单<column name="id" type="INTEGER"/>,而是根据类型族系做语义增强:- 数值型(
Types.INTEGER,Types.BIGINT)→<column name="id" type="long" javaType="java.lang.Long" jdbcType="BIGINT"/> - 字符型(
Types.VARCHAR,Types.CHAR)→<column name="name" type="string" javaType="java.lang.String" jdbcType="VARCHAR" length="64"/> - 时间型(
Types.TIMESTAMP,Types.DATE)→<column name="create_time" type="date" javaType="java.time.LocalDateTime" jdbcType="TIMESTAMP"/> - 主键字段自动添加
<key column="id"/>子节点
- 数值型(
2.3 支持的 XML 场景与对应模板标识
工具内置 5 类常用 XML 模板,通过-t参数指定,每类模板对字段属性的取舍和嵌套深度不同:
| 模板标识 | 输出用途 | 关键特征 | 是否含 JDBC 属性 |
|---|---|---|---|
spring-ds | Spring Bootapplication.yml的 datasource 配置片段 | 仅生成<bean id="dataSource" class="...">及内部<property>,不含表字段 | 否 |
mybatis-rm | MyBatisresultMap定义 | 包含<id>/<result>节点,自动识别主键,jdbcType映射精准 | 是 |
ibatis-sqlmap | iBatis 2.xsql-map-config.xml片段 | 兼容老系统,<parameterMap>和<resultMap>分离,nullValue属性默认填"null" | 是 |
meta-xml | 通用元数据描述(如 ETL 工具接入) | <table name="user">包裹全部<column>,含isPrimaryKey="true"、isNullable="false"属性 | 是 |
jdbc-cfg | 纯 JDBC 连接参数 XML(如 Druid 连接池配置) | 生成<druid:config>根节点,内含url,username,password,driverClassName | 否 |
注意:所有模板均支持
${}占位符注入(如${db.schema}),但占位符解析由调用方负责,工具只做字符串原样保留,避免与 XML 内容冲突。
3. 快速上手:三步完成本地生成(含完整命令与参数说明)
3.1 环境准备:JDK 8+ 与 JDBC Driver 的正确放置方式
工具为纯 Java 命令行程序(JAR 包),无需安装,但JDBC Driver 必须显式加入 classpath—— 这是新手翻车最高发环节。常见错误:把mysql-connector-java-8.0.33.jar放进lib/目录却没在启动命令中引用,导致ClassNotFoundException: com.mysql.cj.jdbc.Driver。
✅ 正确做法(以 MySQL 为例):
# 下载对应版本 driver(务必与数据库大版本匹配) wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.33/mysql-connector-java-8.0.33.jar # 启动命令必须用 -cp 指定 driver + 工具 jar java -cp "mysql-connector-java-8.0.33.jar:xml-gen-tool-1.2.0.jar" \ com.example.xmlgen.Main \ --url "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC" \ --username "root" \ --password "123456" \ --schema "test" \ --table "user_info,user_order" \ --template "mybatis-rm" \ --output "./output/"逻辑说明:
-cp参数中 driver jar 必须在工具 jar之前,因为 JVM 类加载器按顺序扫描;若 driver 在后,工具类先加载成功,但运行时Class.forName("com.mysql.cj.jdbc.Driver")仍会失败。--table支持逗号分隔多个表名,不支持通配符(通配符需用--table-pattern)。
3.2 核心参数详解:哪些必须填,哪些可省略
| 参数 | 是否必需 | 示例值 | 说明 |
|---|---|---|---|
--url | ✅ | jdbc:postgresql://192.168.1.100:5432/mydb | 完整 JDBC URL,必须含?后参数(如currentSchema=public) |
--username | ✅ | app_user | 数据库用户名,明文传入(生产环境建议用配置文件) |
--password | ⚠️(可选,但推荐) | P@ssw0rd! | 若省略,运行时交互式输入,避免密码泄露到 shell history |
--schema | ❌(但强烈建议) | public | 指定 schema 名,MySQL 填数据库名,PostgreSQL/Oracle 填 schema 名;不填则查所有 schema,极慢 |
--table | ❌(二选一) | t_user,t_order | 显式指定表名列表,逗号分隔,无空格 |
--table-pattern | ❌(二选一) | t_% | SQL LIKE 模式,如t_log_2023%,注意%需 shell 转义 |
--template | ✅ | mybatis-rm | 模板标识,见 2.3 表,大小写敏感 |
--output | ✅ | ./gen/ | 输出目录,必须存在且有写权限,工具不会自动创建父目录 |
--encoding | ❌(默认 UTF-8) | GBK | 仅当数据库字符集非 UTF-8 时需指定,如旧版 SQL Server |
参数说明:
--table和--table-pattern互斥,同时出现时--table优先;--output路径末尾不能加/,否则生成的文件路径会变成./gen//UserMapper.xml(双斜杠非法)。
3.3 生成 MyBatis resultMap 的完整示例
假设数据库中有表t_user,结构如下:
CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL, email VARCHAR(128), status TINYINT DEFAULT 1, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );执行命令:
java -cp "mysql-connector-java-8.0.33.jar:xml-gen-tool-1.2.0.jar" \ com.example.xmlgen.Main \ --url "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC" \ --username "root" \ --password "123456" \ --schema "test" \ --table "t_user" \ --template "mybatis-rm" \ --output "./mybatis-maps/"生成文件./mybatis-maps/TUserResultMap.xml内容节选:
<?xml version="1.0" encoding="UTF-8"?> <resultMap id="TUserResultMap" type="com.example.entity.TUser"> <id property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="username" column="username" jdbcType="VARCHAR" javaType="java.lang.String" length="64"/> <result property="email" column="email" jdbcType="VARCHAR" javaType="java.lang.String" length="128"/> <result property="status" column="status" jdbcType="TINYINT" javaType="java.lang.Integer"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/> </resultMap>逻辑说明:
property名称自动转为驼峰(create_time→createTime),jdbcType严格按DatabaseMetaData.getColumns()返回的DATA_TYPE常量映射(Types.TIMESTAMP→"TIMESTAMP"),javaType则根据类型族系智能推断(时间型统一用java.time.*,数值型用包装类)。length属性仅对字符型字段输出,数值型不写(MyBatis 会忽略)。
4. 避坑指南:五个真实踩过的坑与根治方案
4.1 现象:生成的 XML 中jdbcType="OTHER",MyBatis 启动报Unknown JDBC type OTHER
原因:某些数据库(如达梦、人大金仓)的 JDBC Driver 对getColumns()返回的DATA_TYPE值未严格遵循 JDBC 规范,返回了未注册的整数(如20001),工具无法映射为标准jdbcType字符串。
解决:启用类型白名单强制映射。在命令中添加--jdbc-type-mapping参数,传入 JSON 映射文件:
--jdbc-type-mapping "./dm-type-mapping.json"dm-type-mapping.json内容:
{ "20001": "VARCHAR", "20002": "BIGINT", "20003": "TIMESTAMP" }工具启动时会优先查此映射表,未命中才 fallback 到默认逻辑。
4.2 现象:Oracle 表生成的 XML 中column="ID"全是大写,但 Java 实体类字段是id,MyBatis 查询结果为 null
原因:Oracle 默认将未加引号的列名转为大写,getColumns()返回的COLUMN_NAME是"ID",而工具默认直接使用该值作为column属性,未做大小写转换。
解决:使用--column-case参数控制列名大小写策略:
--column-case "lower"→column="id"--column-case "upper"→column="ID"(Oracle 默认)--column-case "original"→ 保持数据库原样(需确保实体类字段名完全匹配)
血泪经验:Oracle 项目务必加
--column-case "lower",否则 90% 的字段映射失败,且错误静默(无报错,只是值为 null)。
4.3 现象:PostgreSQL 的jsonb字段生成为<result ... jdbcType="OTHER"/>,但实际需用PGobject
原因:jsonb是 PostgreSQL 扩展类型,标准 JDBCTypes中无对应常量,Driver 返回DATA_TYPE = 2000(Types.OTHER),但业务代码需用PGobject处理。
解决:自定义类型处理器映射。在模板配置中(非命令行参数),为jsonb字段显式指定javaType和handler:
<!-- 在 mybatis-rm 模板中添加 --> <typeHandler javaType="com.fasterxml.jackson.databind.JsonNode" handler="org.apache.ibatis.type.JdbcTypeHandler"/>工具支持模板内硬编码typeHandler,或通过--custom-handler参数传入映射文件(JSON 格式),将jsonb→JsonNode自动注入。
4.4 现象:生成的spring-dsXML 中driverClassName为空,Spring Boot 启动报Failed to determine embedded database driver class
原因:工具从 JDBC URL 解析driverClassName,但 URL 格式不标准。例如jdbc:mysql://...应解析为com.mysql.cj.jdbc.Driver,但若 URL 是jdbc:mariadb://...,而工具内置映射表未覆盖 MariaDB。
解决:用--driver-class参数显式指定:
--driver-class "org.mariadb.jdbc.Driver"工具内置映射表覆盖 MySQL/PostgreSQL/Oracle/H2,MariaDB、达梦等需手动指定。
4.5 现象:同一张表多次生成,output目录下文件被覆盖,但旧文件的 BOM(Byte Order Mark)头丢失,导致 UTF-8 文件被某些编辑器识别为 ANSI
原因:JavaFileWriter默认不写 BOM,而 Windows 记事本等工具依赖 BOM 识别 UTF-8。
解决:启用 BOM 输出开关:
--bom true工具内部改用OutputStreamWriter+StandardCharsets.UTF_8并手动写入0xEF,0xBB,0xBF三字节 BOM。此参数默认false,Windows 环境下建议始终开启。
5. 进阶技巧:如何让生成的 XML 无缝接入 Spring Boot 多数据源工程
5.1 多数据源场景下的 XML 组织策略
当工程需对接 MySQL(业务库)和 PostgreSQL(报表库)时,不能简单把所有表混在一个mybatis-rm模板里。正确做法是:按数据源拆分生成目录,再用 MyBatis 的@MapperScan指定不同 basePackage。
步骤:
- 为 MySQL 生成:
java -cp "mysql-connector-java-8.0.33.jar:xml-gen-tool-1.2.0.jar" \ com.example.xmlgen.Main \ --url "jdbc:mysql://mysql-host:3306/app_db" \ --username "app_rw" \ --password "pwd1" \ --schema "app_db" \ --table-pattern "t_user%" \ --template "mybatis-rm" \ --output "./src/main/resources/mapper/mysql/"- 为 PostgreSQL 生成:
java -cp "postgresql-42.6.0.jar:xml-gen-tool-1.2.0.jar" \ com.example.xmlgen.Main \ --url "jdbc:postgresql://pg-host:5432/report_db" \ --username "report_ro" \ --password "pwd2" \ --schema "public" \ --table-pattern "rpt_%" \ --template "mybatis-rm" \ --output "./src/main/resources/mapper/postgres/"- 在 Spring Boot 中配置两个 SqlSessionFactory:
@Configuration public class MyBatisConfig { @Bean("mysqlSqlSessionFactory") public SqlSessionFactory mysqlSqlSessionFactory( @Qualifier("mysqlDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/mysql/*.xml")); return factoryBean.getObject(); } @Bean("postgresSqlSessionFactory") public SqlSessionFactory postgresSqlSessionFactory( @Qualifier("postgresDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/postgres/*.xml")); return factoryBean.getObject(); } }关键点:
setMapperLocations()的路径必须与--output目录严格对应,且 XML 文件名需符合 MyBatis 命名规范(如TUserMapper.xml),工具生成的文件名已自动处理(表名转 PascalCase)。
5.2 动态字段注入:为 XML 添加环境相关属性
生产环境常需在 XML 中注入${env}占位符,如<property name="url" value="${mysql.url}"/>。工具支持--properties-file参数读取.properties文件,并在生成时替换。
创建env-dev.properties:
mysql.url=jdbc:mysql://dev-db:3306/test mysql.username=dev_user执行命令:
--properties-file "./env-dev.properties"工具会扫描所有 XML 模板中的${key}占位符(如${mysql.url}),若key存在于 properties 文件中,则替换为对应值;否则保留原占位符(供 Maven Filter 或 Spring Profile 处理)。
5.3 与 CI/CD 流水线集成:GitLab CI 中自动生成并提交
在.gitlab-ci.yml中添加 job,每次合并到develop分支时自动生成最新 XML:
generate-mappers: stage: build image: openjdk:11-jre-slim before_script: - apt-get update && apt-get install -y wget - wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.33/mysql-connector-java-8.0.33.jar script: - java -cp "mysql-connector-java-8.0.33.jar:xml-gen-tool-1.2.0.jar" \ com.example.xmlgen.Main \ --url "$MYSQL_URL" \ --username "$MYSQL_USER" \ --password "$MYSQL_PASSWORD" \ --schema "app" \ --table-pattern "t_%" \ --template "mybatis-rm" \ --output "./src/main/resources/mapper/generated/" - git config --global user.email "ci@example.com" - git config --global user.name "GitLab CI" - git add ./src/main/resources/mapper/generated/ - git commit -m "chore: auto-generate mappers from DB schema [skip ci]" - git push https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.example.com/group/project.git HEAD:develop only: - develop注意:
$MYSQL_URL等变量需在 GitLab 项目 Settings > CI/CD > Variables 中预设为 Protected & Masked,避免密码泄露;[skip ci]防止触发无限循环构建。
从那以后我每次接手新数据库项目,第一件事就是写好generate.sh脚本,把 JDBC URL、用户密码、表模式全参数化,然后加入 Makefile 的make gen目标。这样团队里任何人 checkout 代码后,只需改一行.env文件,make gen就能生成全量 XML,再也不用担心谁手抖漏改了一个<result>节点。希望帮到你。
本文还有配套的精品资源,点击获取