简介这款 XML 文件自动生成工具面向需要从数据库批量导出结构化 XML 的 Java 开发者与数据交换场景借助 JDBC 连接数据源将 SQL 查询结果按元素与属性组织成 XML 文档减少手工编写和校验的重复工作。压缩包共 4 个文件约 144KB以 exe 可执行程序为主辅以 properties、ini 配置文件和 xml 数据源描述分别承担运行入口、数据库连接参数与初始化配置、数据源结构说明等用途体积轻便便于本地部署和快速试用。目前已有 2354 人学习下载说明其在数据库转 XML 类小工具中具有一定参考价值。工具围绕表结构到 XML 的映射、关系数据的嵌套处理以及根元素、编码格式、Schema 校验等输出定制展开适合需要频繁生成 XML、又不愿逐行手写标签的开发者用于提高效率并降低语法出错概率。对需要把查询结果稳定落成 XML 的日常开发与数据交付任务较为实用。1. 把 xml 文件自动生成工具接到 jdbc 数据源先别急着点生成第一次看到“xml 文件自动生成工具”这个标题很容易以为它只是把几个模板字符串拼一拼。真正把它接到 jdbc datasource 上之后价值才显出来它读的是表、字段、主键、类型这些元数据输出的是能直接被解析和比对的 xml 配置。对表多、字段改得勤、多人维护同一套 xml 的团队这类工具能把手写重复结构的时间压下去对只有两三张表的一次性项目手工写反而更省事。下面按我拆包和落地的顺序把连接、生成、模板、排错和验证讲透。2. 拆开 xml 文件自动生成工具jdbc 元数据到 datasource 配置的映射这类工具的核心链路并不复杂用 jdbc 连上 datasource从数据库元数据里读出表、列、主键和类型信息再把这些信息塞进模板最后写出 xml 文件。真正容易翻车的地方不在“循环拼字符串”而在元数据读取的边界schema 和 catalog 的取值、大小写、字段顺序、类型映射、注释里的特殊字符每一个都可能让生成结果看起来对、跑起来错。所以拿到资源后我一般先不看模板先看它怎么读 jdbc 元数据。2.1 生成器到底读什么jdbc 元数据、datasource 参数和 xml 模板jdbc datasource 在生成器眼里不是“数据库连接”这么简单它至少提供三类输入连接参数、元数据查询条件、输出模板。连接参数决定连到哪个库、用哪个账号元数据查询条件决定读哪些 schema、哪些表、哪些列模板决定最终 xml 的骨架和命名。常见做法是用DatabaseMetaData的getTables、getColumns、getPrimaryKeys三个入口把表清单和列清单拉出来再落成一个中间模型。下面这段 Java 代码是元数据读取的最小示例。它不依赖具体持久层框架只演示生成器内部最该先做对的一步try (Connection conn DriverManager.getConnection(url, user, password)) { DatabaseMetaData meta conn.getMetaData(); // schemaPattern 用当前 datasource 的 schematableNamePattern 用通配符 try (ResultSet tables meta.getTables(null, schema, %, new String[]{TABLE})) { while (tables.next()) { String tableName tables.getString(TABLE_NAME); String remark tables.getString(REMARKS); // 读列第 4 个参数是列名通配符% 表示全部列 try (ResultSet cols meta.getColumns(null, schema, tableName, %)) { while (cols.next()) { String column cols.getString(COLUMN_NAME); int sqlType cols.getInt(DATA_TYPE); // java.sql.Types 的整型值 String typeName cols.getString(TYPE_NAME); // 厂商类型名不要直接当 jdbcType int ordinal cols.getInt(ORDINAL_POSITION); // 稳定排序要靠它 int nullable cols.getInt(NULLABLE); // 把这些字段写入中间模型后续交给模板渲染 } } } } }这段代码里schema不是所有数据库都放在同一个参数位有的实现把库名放在 catalog有的放在 schema。getColumns的第 4 个参数是列名通配符不要误传成表名。DATA_TYPE是标准整型常量TYPE_NAME是厂商返回的名字两者要一起看不能只拿TYPE_NAME直接映射成 xml 里的jdbcType。ORDINAL_POSITION很重要生成结果要稳定列顺序就不能依赖驱动返回顺序。参数作用常见误设jdbc.url指定 datasource 地址和目标库把测试库地址带进生产生成jdbc.user元数据读取账号用业务写账号权限过大jdbc.password连接凭据明文写进模板或输出目录schema表所在命名空间和 catalog 混用导致查不到表include/exclude表过滤规则只写包含规则备份表也被生成templatexml 骨架模板里混入业务判断后期难维护outputDir生成文件落点直接覆盖人工维护目录2.2 资源里的最小可运行链路我把这类工具拆成三层看输入层、元数据层、渲染层。输入层只认 datasource 配置和过滤规则元数据层负责把表、列、主键、类型整理成稳定模型渲染层只做变量替换和循环展开。三层分开以后排错会快很多生成空文件先查输入层和元数据层xml 结构不对再查模板字段类型不对查映射表。最怕的是一份脚本从连接一直写到文件出错时只能靠猜。拿到资源后先跑最小链路连一个只有两三张表的测试 datasource只生成一张表不接模板定制不开启覆盖。这个过程不是为了产出最终文件而是确认三件事连接是否通、schema 是否写对、列信息是否能读全。最小链路跑通之后再逐步加 include 规则、加模板、加类型覆盖。顺序反过来一旦生成结果不对你很难判断是连接问题、过滤问题还是模板问题。2.3 为什么不是“一个脚本就够”表少的时候手写 xml 确实更快。但表一多重复成本不只是“第一次写”而是字段变更后的同步成本。一个字段改名手写要改实体、改结果映射、改查询片段、改校验逻辑生成器至少能把结构部分统一产出。更关键的是可审计生成结果可以 diff手写结果经常只能靠人眼扫。生成器不是让人不看 xml而是把人的注意力从重复结构挪到差异和边界上。另一个容易被忽略的点是团队协作。手写 xml 时每个人对命名、缩进、空值处理都有自己的习惯生成器把命名规则和模板固定下来输出风格会趋于一致。代价是前期要花时间调模板和映射表这部分不能省。如果只是临时导一次数据没必要上生成器如果这套 xml 会跟着表结构长期维护生成器就值得接进流程。3. 从 jdbc datasource 到第一份 xml六步跑通生成流程这一章按实际落地顺序走先准备只读连接再 dry-run 看表再生成第一版再做 xml 结构校验再做基线对比最后接到构建前检查。每一步都别跳尤其是 dry-run 和基线对比能提前拦住大部分“生成成功但内容不对”的问题。3.1 准备 datasource 连接参数先只读元数据账号第一步不是写模板是把 datasource 配置准备好。账号只给元数据读取权限不要给写权限密码不要写死在配置文件里用环境变量注入。下面是一个通用的 properties 配置字段名可以按资源里的实际配置调整# conf/datasource.properties jdbc.drivercom.example.Driver jdbc.urljdbc:demo://127.0.0.1:1521/app jdbc.userxml_reader jdbc.password${XML_GEN_DB_PASSWORD} schemaappjdbc.driver是驱动类名jdbc.url指向目标 datasourcejdbc.user和jdbc.password只用于元数据读取。${XML_GEN_DB_PASSWORD}这种写法要求启动时注入环境变量避免把密码提交到版本库。schema要写实际命名空间不要凭感觉填。配置写完后先用数据库客户端连一次确认账号能看到目标表如果客户端都看不到生成器更不可能读到。提示生成账号只给元数据读取权限不要给写权限。生成器只需要读表结构和列信息不需要改数据。3.2 用命令行先做 dry-run只打印表名和字段数dry-run 的价值是“不写文件也能看到生成器准备处理什么”。我一般先让它列出表名、字段数、主键列确认 include 和 exclude 没有漏掉或多收java -jar xml-gen.jar \ --config conf/datasource.properties \ --schema app \ --include t_*,biz_* \ --exclude *_bak,*_tmp \ --dry-run--config指定 datasource 配置--schema指定命名空间--include和--exclude用通配符过滤表。--dry-run只读元数据、不落盘。输出里重点看三件事表数量是否符合预期、每张表的字段数是否明显偏少、主键是否被识别到。如果表数量为 0优先查 schema 大小写和 catalog 参数如果字段数偏少查账号权限和列过滤规则。3.3 生成第一版 xml先关掉覆盖dry-run 通过后再生成第一版。第一版不要开覆盖避免把已有文件冲掉java -jar xml-gen.jar \ --config conf/datasource.properties \ --schema app \ --include t_*,biz_* \ --exclude *_bak,*_tmp \ --template templates/resultmap.xml.tpl \ --out generated/xml \ --overwritefalse--template指定 xml 骨架--out指定输出目录--overwritefalse表示目标文件已存在时直接报错。这个参数看起来保守实际很有用第一次落地时人工维护的 xml 和生成目录最好分开确认生成结果稳定后再谈合并。生成完成后先抽三张表看主键有没有写进去、字段顺序是否稳定、类型名是否为空。3.4 做 xml 结构校验别等运行时报错生成成功不等于 xml 可用。至少要过一遍解析和结构检查。下面这段 Python 只做三件事遍历输出目录、解析 xml、检查根节点和结果节点是否存在import sys import xml.etree.ElementTree as ET from pathlib import Path root_dir Path(sys.argv[1]) files list(root_dir.rglob(*.xml)) if not files: raise SystemExit(no xml generated) for f in files: tree ET.parse(f) root tree.getroot() # 根节点按模板实际输出调整这里以 mapper 为例 if root.tag ! mapper: raise SystemExit(fbad root: {f} - {root.tag}) columns root.findall(.//result) if not columns: raise SystemExit(fempty result map: {f}) print(fok files{len(files)})sys.argv[1]是生成目录rglob(*.xml)会递归找文件。ET.parse能抓出未闭合标签、非法字符这类硬错误根节点检查和结果节点检查能抓出模板写错导致的空文件。这里的mapper和result只是示例节点名实际要按资源模板的根节点调整。校验脚本不要写得太聪明先保证结构可用再谈业务语义。3.5 和基线做 diff把“看起来一样”变成可审差异生成器最怕的是“每次都成功但输出悄悄变了”。我习惯保留一份基线目录每次生成到临时目录后做 diffrm -rf target/xml-check java -jar xml-gen.jar \ --config conf/datasource.properties \ --schema app \ --include t_*,biz_* \ --exclude *_bak,*_tmp \ --template templates/resultmap.xml.tpl \ --out target/xml-check \ --overwritetrue diff -ru baseline/xml target/xml-check target/xml.diff || truebaseline/xml是上一次确认过的输出target/xml-check是本次临时输出。diff -ru会递归比较并输出差异|| true只是让命令在存在差异时继续执行方便后续查看文件。差异文件不要直接忽略重点看三类变化新增或删除表、字段类型变化、命名变化。如果差异来自模板调整就更新基线如果差异来自数据库结构变化就先确认变更是否已经评审。3.6 接到构建前检查只校验不自动覆盖最后一步是把生成和校验接进构建前检查。注意这里不是让构建过程自动覆盖已提交的 xml而是生成到临时目录后做校验和 diffjava -jar xml-gen.jar \ --config conf/datasource.properties \ --schema app \ --include t_* \ --template templates/resultmap.xml.tpl \ --out target/generated-xml \ --overwritetrue python scripts/check_xml.py target/generated-xml diff -ru src/main/resources/xml target/generated-xml target/generated-xml.diff test ! -s target/generated-xml.difftarget/generated-xml是临时输出src/main/resources/xml是人工确认后的目录。test ! -s表示 diff 文件为空才通过。这样构建不会悄悄改文件但会在结构漂移时拦住。实际落地时可以把“更新基线”做成单独的手动步骤避免生成器一跑就把差异吞掉。4. 模板与命名策略让自动生成的 xml 不返工生成器能不能长期用八成看模板和命名。模板负责结构命名负责可读性类型映射负责正确性。三件事混在一起后面改一个字段名都可能牵动一堆判断。我的习惯是模板只做排版命名规则单独配置类型覆盖单独一张表。这样出问题时能快速定位到底是哪一层错了。4.1 模板变量表table、column、jdbcType、datasource模板里不要塞业务判断只保留变量替换和循环。下面是一个结果映射模板的简化写法!-- templates/resultmap.xml.tpl -- resultMap id${table.alias}Map type${table.className} id column${pk.column} property${pk.property}/ ${columnFragments} /resultMap${table.alias}来自表名转换${table.className}来自命名规则${pk.column}和${pk.property}来自主键元数据${columnFragments}是生成器循环展开的列片段。模板本身不判断“这张表要不要特殊处理”特殊处理放到覆盖表里。这样模板改动的影响面可控生成结果也更容易 diff。变量来源说明${table.name}元数据表名保留原始表名便于追溯${table.alias}命名规则用于 xml 节点 id${table.className}命名规则用于类型名注意保留字${column.column}元数据列名写回 xml 的 column${column.property}命名规则写回 xml 的 property${column.jdbcType}类型映射表不要直接取厂商类型名${pk.column}主键元数据复合主键要循环处理4.2 命名转换表名、列名、类名要分开算表名转类名、列名转属性名、xml 节点 id 经常不是同一套规则。下面这段 Python 只做下划线转驼峰并给保留字加后缀实际使用时可以把规则抽到配置里import re RESERVED {order, group, desc, user} # 示例保留字按目标框架补 def to_camel(raw: str) - str: parts [p for p in re.split(r[_\-\s], raw.strip().lower()) if p] if not parts: return unknown name parts[0] .join(p[:1].upper() p[1:] for p in parts[1:]) return name _ if name in RESERVED else name print(to_camel(biz_order_item)) # bizOrderItem print(to_camel(user)) # user_re.split把下划线、短横线、空白都当作分隔符parts[0]保持小写后面的部分首字母大写。保留字加后缀是为了避免生成出来的属性名和语言关键字冲突。这里没有处理全大写缩写如果表里存在类似api_url的字段要决定是生成apiUrl还是apiURL这个规则一旦定下就不要在模板里临时改。4.3 jdbcType 映射不要靠猜用元数据加覆盖表jdbcType是最容易拍脑袋的地方。元数据里的TYPE_NAME是厂商返回的名字不同驱动可能返回不同大小写或别名DATA_TYPE是标准整型值但也不能覆盖所有边界。稳妥做法是默认映射走一张表特殊列再走覆盖表。下面是一个 yaml 覆盖示例jdbcType: default: OTHER byName: VARCHAR: VARCHAR DECIMAL: DECIMAL TIMESTAMP: TIMESTAMP CLOB: CLOB byColumn: app.t_order.amount: DECIMAL app.t_order.remark: CLOBbyName按元数据里的类型名匹配byColumn按“schema.表.列”精确覆盖。default: OTHER是兜底避免未知类型直接生成空值。实际使用时先把所有列的类型映射打印出来人工扫一遍异常项再把异常项写进byColumn。不要为了省事把所有类型都写成字符串后面排查空值和精度问题会更痛苦。4.4 datasource 多环境拆分dev/test/prod 只差连接串多环境不要把连接串写进模板。模板只认变量datasource 配置按环境拆文件。下面是一个测试环境配置# conf/datasource-test.properties jdbc.drivercom.example.Driver jdbc.urljdbc:demo://test-host:1521/app jdbc.userxml_reader jdbc.password${XML_GEN_DB_PASSWORD} schemaapp启动时注入密码再指定配置文件export XML_GEN_DB_PASSWORD*** java -jar xml-gen.jar \ --config conf/datasource-test.properties \ --schema app \ --out generated/test \ --overwritetrueexport只影响当前会话适合本地和流水线临时注入。生产环境的生成任务最好只读输出目录也不要和人工维护目录混在一起。多环境配置的重点不是“能连上”而是“同一套模板在不同 datasource 上生成的结构一致”所以每次切换环境都要跑一遍 dry-run 和 diff。5. 避坑与排查xml 文件自动生成工具最常见的五处翻车生成器用起来顺的时候很顺一旦翻车现象往往很隐蔽文件生成了但内容是空的xml 能解析但字段类型不对这次和上次看起来一样diff 却一大片。下面五条是我踩过的典型坑按“现象、原因、解决”拆开写。5.1 生成出来是空文件schema 和 catalog 弄反了现象命令执行成功输出目录里也有文件但 xml 里没有字段或者只有根节点。原因通常不是模板坏了而是 jdbc 元数据查询条件没命中表。不同数据库对 catalog 和 schema 的摆放不一样代码里把库名传给 schema或者大小写不一致getTables就会返回空结果。另一种情况是账号只有连接权限没有元数据读取权限。解决先用 dry-run 只看表清单确认表数量再把 schema 和 catalog 分别打印出来对照。表名大小写不要靠猜元数据返回什么就按什么匹配。账号权限单独验证一次别把“连上了”当成“能读结构”。如果工具支持--catalog参数先显式传空值测试再决定是否启用。5.2 字段类型全变成字符串jdbcType 映射偷懒了现象生成的 xml 里所有列都是同一个类型或者大文本、金额字段被写成普通字符串。原因多半是模板直接取了TYPE_NAME或者映射表只有默认值没有按DATA_TYPE做兜底。驱动返回的类型名可能有别名直接当jdbcType用短时间看不出问题运行到精度或空值场景就翻车。解决把DATA_TYPE、TYPE_NAME、列名、最终jdbcType打一张对照表先人工扫一遍。默认映射只做兜底金额、时间、大文本这些关键类型走byColumn覆盖。模板里不要写“如果是某张表就特殊处理”特殊处理放覆盖表后面换库时更容易迁移。5.3 xml 解析报错注释或默认值里的特殊字符没转义现象生成器不报错但 xml 解析器报“格式错误”或“实体未定义”。原因通常是表注释、列注释、默认值里带了、、模板直接原样输出。另一种情况是注释里出现连续短横线或者 CDATA 结束符被拼进内容解析器同样会拒绝。解决模板输出文本节点前统一做 xml 转义注释内容要么过滤要么走安全的注释生成逻辑。不要看到解析报错就手工改生成文件下一次生成还会覆盖。校验脚本里加上ET.parse让这类问题在生成阶段暴露而不是等到运行期。5.4 每次生成的字段顺序都在变没有用 ordinal 排序现象两次生成的内容字段一样diff 却显示大量行移动。原因通常是生成器直接按驱动返回顺序写列或者用了不稳定的哈希集合。元数据返回顺序在不同驱动、不同版本、不同查询条件下可能变化靠它保证稳定不靠谱。解决列信息落模型时保留ORDINAL_POSITION输出前按表名、列序号做稳定排序复合主键也按主键序号排序。模板里不要用无序集合遍历。排序规则写进生成器不要写在模板里否则换模板又会乱。5.5 人工改过的 xml 被覆盖生成目录和手工目录混在一起现象生成器一跑之前手工补的查询片段没了。原因不是工具坏而是输出目录直接指向了人工维护目录并且开了覆盖。生成器擅长产出结构不擅长保留人的临时修改一旦混在一起每次生成都像一次高风险操作。解决生成目录和手工目录分开生成结果先落临时目录diff 通过后再人工合并。不要直接编辑生成文件需要定制的内容放到覆盖模板或独立片段里。覆盖开关默认关闭批量更新时再显式打开。血泪经验是只要生成器能写生产目录就一定会有人误触。6. 进阶验证把生成 xml 的差异压到可审6.1 用固定样例库卡住回归我习惯准备一个专门用于验证的 datasource只放四类表复合主键、字段名含下划线和数字、存在大文本字段、存在保留字列名。每次改模板前先跑一遍生成把输出目录和基线做 diff。基线不要自动更新只有确认差异是预期变更才提交。这样模板里的一个循环写错通常在第一张表就暴露而不是等到业务联调。6.2 先冻结命名再改模板命名规则一旦进入多人协作就不要在模板里临时加判断。表名转类名、列名转属性名、jdbcType 覆盖表三件事分开配置模板只负责排版。否则半年后看到a_b和aB两套输出很难判断哪次生成是对的。我一般把命名配置单独放进一个只读文件模板变更必须附带基线 diff。从那以后我每次接新数据源都先跑 dry-run 和固定样例库再决定是否覆盖已有 xml。工具能省掉重复劳动但省不掉对元数据和差异的复核。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?