MyBatis Generator XML配置实战:从模板到代码生成全解析

发布时间:2026/10/15 22:56:23
MyBatis Generator XML配置实战:从模板到代码生成全解析 从 MyBatis Generator 的 XML 配置看起很多人第一次用这个工具时对着配置模板一头雾水classPathEntry是什么意思table里的domainObjectName到底要不要写全限定名targetProject该填什么路径我又不希望生成Example类该删哪里这篇就以我实际项目中反复打磨过的一套配置模板为主线把每个节点的含义、每个属性的效果、以及运行命令的细节全部盘一遍。无论你是第一次接触 MBG 的初学者还是被配置文件里的exception报错折磨过的使用者这篇都值得存下来对照操作。1. 配置模板的整体设计思路1.1 为什么需要一份清晰的配置模板MyBatis Generator下文简称 MBG本质是一个代码生成器它根据数据库表结构生成四类产物实体类Model、Mapper 接口Dao、XML 映射文件Mapper XML以及可选的 Example 查询条件类。它解决的核心痛点是CRUD 基础代码反复手写、容易出错、命名不统一。但 MBG 不是开箱即用的“傻瓜工具”它的行为完全由一份 XML 配置驱动。同一个数据库表配置不同生成出来的代码风格完全不同。我把配置模板的重要性看得和代码本身一样高——它是整个生成过程的地基。配置写得乱后期生成的代码就乱配置写得好团队里任何人拉下来都能在一个命令之内得到风格一致的代码。很多团队用 MBG 的方式是找一个网上现成的配置文件改一下连接串和表名就开跑当时可能感觉“跑通了”。但这些模板往往内容冗余可能残留了旧版本的 DTD 引用、多余的分页插件配置、或者用了已经不推荐使用的属性。真遇到问题定位起来很困难。所以我对配置模板的要求是每个节点都清楚它存在的意义不需要的节点一律不出现。1.2 配置文件的核心作用域一套完整的 MBG 配置分为两大作用域全局配置和表级配置。全局配置控制生成代码的“气质”编码格式、目标工程目录、生成的注释风格、是否生成 Example表级配置则决定每一张表具体怎么映射主键生成策略、字段类型转换、表名与类名的对应关系。理解这个分层关系之后调试配置就会有个清晰的思路如果所有表的生成行为都不符合预期问题大概率在全局配置如果只有某一张表设置不了主键或者字段映射错误问题大概率在表级配置。这个诊断思路帮我省下过大量排查时间。2. 全局配置根节点与 context 的深度解读2.1 DTD 声明与根节点任何一份 MBG 配置文件的第一行必然是 XML 声明和 DTD 声明。DTD 引用我从引入http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd这是 MBG 官方定义配置语法格式的文档类型描述。使用新版 MBG 时同样可以沿用这个 1.0 的 DTD因为配置元素的整体语法结构始终保持了向后兼容。要注意的是很多教程里的 DTD 地址是http://mybatis.org/dtd/...不带www这个地址在国内网络环境下偶尔访问超时——但不影响 MBG 生成代码因为解析器会优先读取本地内置的 DTD 定义网络请求失败时不会中断生成过程。根节点generatorConfiguration下面通常直接挂context。我在网上下载过的一些模板还会保留properties节点用于读取外部 properties 文件。这个节点在小项目里用处不大但它能让数据库连接信息与生成规则分离。我推荐的做法是将数据库连接地址、用户名、密码写入一个独立的generator.properties文件然后在根节点用properties resourcegenerator.properties/引入。这样在多人协作时每个人可以保留自己的数据库配置而不必改动公共的 XML 模板。这算是我多年使用下来体验最舒适的组织方式。2.2 context 节点的关键属性context节点是整个配置文件的“发动机”它的核心属性包括id任意字符串只用于标识不同 context生成后的内容不会直接使用这个 id但不同 context 之间必须唯一。targetRuntime指定生成代码所基于的运行时框架。最常用的是MyBatis3生成的是标准 MyBatis 风格的 Mapper 与 XML如果项目使用了 MyBatis 的分页插件PageHelperMBG 会自动带上分页所需的方法签名实际上 PageHelper 有自己的拦截器MBG 只是生成兼容的方法。若选择MyBatis3Simple则生成精简的 Mapper 接口和 XML少了 Example 相关的复杂方法代码量大幅减少是我个人偏爱的配置。defaultModelType控制实体类的生成方式。我对这个属性踩过坑默认值为conditional意思是如果一个表有联合主键可能把主键字段独立到一个类里而flat则把表的所有字段放在唯一一个实体类中。如果不希望生成“主键类字段类BLOB类”这种碎片化的实体结构推荐显式设置为flat。我见过不少项目使用默认值后实体类里莫名多了一个“Key”类排查了半天才发现是这个属性在起作用。2.3 注释配置与插件注册commentGenerator节点控制生成代码中的注释内容。默认情况下MBG 会在每个类、每个字段前生成一段冗长的注释其中包含“This class was generated by MyBatis Generator”等提示文本。如果生成的代码需要交给团队维护我通常的做法是自定义一个注释生成器或者直接配置如下commentGenerator property namesuppressAllComments valuetrue/ /commentGeneratorsuppressAllComments设为true后所有自动生成的注释都会被抑制。需要注意这个属性还会影响 Example 类里的criteria注释但不会影响 XML 映射文件中的必要 SQL 语句。如果你既不希望代码里出现英文注释又希望保留一点可读性可以在使用自定义 Java 类的方式生成中文注释这个在后面的高级配置里我会展开讲。插件节点plugin的使用场景通常包括MyBatis 分页插件生成器插件、序列化插件、ToString 插件、Equals/HashCode 插件。配置插件时列表顺序有一定讲究部分插件比如覆盖主键方法的插件对生成顺序有依赖。对于入门阶段的项目保留plugin typeorg.mybatis.generator.plugins.RowBoundsPlugin/即可支持分页查询其余插件按需添加不要为了齐全而全上——插件越多生成的代码变化越大排查问题越困难。3. 三个生成器模型、映射文件、客户端接口这一节是配置模板中最长的部分也是最容易出问题的地方。MBG 要求context内部必须依次声明连接工厂、模型生成器、映射 XML 生成器和客户端Mapper/Dao生成器。顺序不能随意颠倒否则运行时会直接报 XML 配置错误。3.1 jdbcConnection连接数据库的“入口”jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURL${jdbc.url} userId${jdbc.username} password${jdbc.password}/使用 MySQL 8.x 及以上版本时驱动类名必须写com.mysql.cj.jdbc.Driver如果继续沿用旧的com.mysql.jdbc.Driver会直接连接失败。连接串中要显式声明 UTF-8 编码和服务器时区比如jdbc:mysql://localhost:3306/yourdb?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai这里有一个容易忽略的关键点MBG 执行的数据库连接过程中会读取数据库的元数据表结构、主键、字段注释。如果数据库账号权限不足比如没有SELECT权限或者没有SHOW相关权限生成过程会报“Could not get table info”之类的错。我经历过的某次项目中DBA 给了应用账号读写权限但没有元数据查询权限MBG 始终报错换成具有相应权限的账号后立刻成功。3.2 javaModelGenerator实体类生成规格javaModelGenerator targetPackagecom.example.model targetProjectsrc/main/java property nameenableSubPackages valuetrue/ property nametrimStrings valuetrue/ property nameimmutable valuefalse/ /javaModelGeneratortargetPackage指定包名targetProject指定源码根目录。我见过很多初学者的疑问“为什么生成到别的目录了”——那是因为targetProject填的是相对路径而 MBG 在命令行运行时的工作目录决定了相对路径的基准。建议总是填写绝对路径或者通过命令行参数-DtargetProject...覆盖避免“放在哪”的困惑。enableSubPackages设为true时会根据数据库的 schema/catalog 再拼接一层包名多 schema 的库建议打开普通单库项目保持false更容易控制结构。trimStrings是数据库字段为char类型时是否对传入的字符串自动trim()。业务上对字符串首尾空格敏感的项目这个属性必须打开。值得注意的是immutable属性。默认false生成带 setter 的普通 POJO如果设为true则生成只有构造函数没有 setter 的不可变对象。表面上看起来“代码更安全”但 MyBatis 反射填值对无 setter 对象的支持依赖构造器参数名映射配置复杂时会踩坑所以绝大多数项目我建议保留默认值。3.3 sqlMapGeneratorXML 映射文件生成规格sqlMapGenerator targetPackagemapper targetProjectsrc/main/resources property nameenableSubPackages valuefalse/ /sqlMapGenerator这里我最想强调一个常见认知错误targetPackage填的是“包路径风格”的目录结构比如com.example.mapperMBG 会生成src/main/resources/com/example/mapper这样的目录。如果将它填成mapper则生成的 XML 直接放在resources/mapper下通常来说更简洁。targetProject如果是 Maven 项目记得指向src/main/resources而不是src/main/java。XML 文件放在 java 源码目录在大多数构建环境并不会报错但会让项目结构混乱也违反团队约定。3.4 javaClientGeneratorMapper 接口生成规格javaClientGenerator typeXMLMAPPER targetPackagecom.example.dao targetProjectsrc/main/java property nameenableSubPackages valuefalse/ /javaClientGeneratortype可选XMLMAPPER生成独立 XML接口、ANNOTATEDMAPPER接口中直接写注解 SQL和MIXEDMAPPER简单 SQL 注解 复杂 SQL XML。最常用也是我推荐的是XMLMAPPER团队审查 SQL 比较方便SQL 调整也无需重新编译 Java 代码。targetPackage的命名通常会根据项目分层习惯设置。我见过一些项目直接把 mapper 接口和实体类放在同一个包下几百个文件堆积在同一个目录维护体验非常差。即便 MBG 能生成规范仍是开发者的责任。3.5 table 节点的精细配置table节点是配置中使用频率最高的子节点每一张需要生成的表都要声明一次。核心属性如下table tableNameuser domainObjectNameUserInfo enableInserttrue enableUpdateByPrimaryKeytrue enableDeleteByPrimaryKeytrue enableSelectByPrimaryKeytrue enableSelectByExampletrue enableCountByExampletrue enableUpdateByExampletrue enableDeleteByExampletrue selectByExampleQueryIdtruetableName数据库表名必填。domainObjectName实体类名。强烈建议显式声明而不是靠 MBG 自动推算。自动推算的结果在表名有下划线时往往不符合团队命名规范。enableXxx系列控制生成哪些方法。如果项目不需要 Example 查询将enableSelectByExample等属性设为false再配合targetRuntimeMyBatis3Simple生成的代码量会大幅减少。generatedKey如果表的主键是自增的需要在这里配置 JDBC 获取自增主键的方法generatedKey columnid sqlStatementMySql identitytrue/sqlStatement根据数据库类型填写MySQL 写MySqlPostgreSQL 写PostgreSQL。配置后 MBG 生成的insert语句会自动包含useGeneratedKeystrue和keyPropertyid彻底解决自增主键回填问题。3.6 字段映射与类型转换当数据库字段类型与 Java 类型不能自动映射时通过columnOverride手动修正columnOverride columnremark javaTypejava.lang.String jdbcTypeVARCHAR/需要处理数字精度问题的场景也很常见比如数据库decimal(10,4)自动映射为BigDecimal业务上希望使用Double可用javaTypejava.lang.Double覆盖。修改基础类型映射会直接影响所有生成代码的 SQL 参数类型务必要测试后再提交。此外还有两个常用属性ignoreColumn用于排除某列不生成tableName支持 SQL 通配符比如tableName%生成所有表但在实际项目中我基本不用通配符宁可显式列出每张表宁可长一点也不愿意误生成历史遗留表。4. 一套可直接复用的完整配置文件下面给出一个贴近实战的完整配置。这是一份我在某中型项目中使用过的模板按需删减后用于快速交付。?xml version1.0 encodingUTF-8? !DOCTYPE generatorConfiguration PUBLIC -//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd generatorConfiguration properties resourcegenerator.properties/ context idmysqlContext targetRuntimeMyBatis3Simple defaultModelTypeflat !-- 关闭自动生成的注释 -- commentGenerator property namesuppressAllComments valuetrue/ /commentGenerator !-- 数据库连接 -- jdbcConnection driverClass${driverClass} connectionURL${connectionURL} userId${userId} password${password}/ !-- 实体类 -- javaModelGenerator targetPackage${model.package} targetProject${model.project} property nameenableSubPackages valuefalse/ property nametrimStrings valuetrue/ /javaModelGenerator !-- XML映射文件 -- sqlMapGenerator targetPackage${xml.package} targetProject${xml.project} property nameenableSubPackages valuefalse/ /sqlMapGenerator !-- Mapper接口 -- javaClientGenerator typeXMLMAPPER targetPackage${dao.package} targetProject${dao.project} property nameenableSubPackages valuefalse/ /javaClientGenerator !-- 表配置显式列出不开 Example -- table tableNamesys_user domainObjectNameSysUser generatedKey columnid sqlStatementMySql identitytrue/ /table table tableNamesys_role domainObjectNameSysRole generatedKey columnid sqlStatementMySql identitytrue/ /table /context /generatorConfiguration配套的generator.properties文件内容如下driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/yourdb?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai userIdroot password123456 model.packagecom.example.model model.projectsrc/main/java xml.packagemapper xml.projectsrc/main/resources dao.packagecom.example.dao dao.projectsrc/main/java我在实际项目中常把xml.package直接设为mapper而不是完整的包路径。配置越短后面维护越轻松。如果你使用 Maven直接在pom.xml中挂载mybatis-generator-maven-plugin运行时执行mvn mybatis-generator:generate即可。这个插件会把配置文件的相对路径基准自动定为 Maven 项目根目录所以targetProject一般写src/main/java这种相对路径即可。5. 常见问题与排查技巧实录5.1 连接数据库失败错误信息是中文乱码经常有人生成时控制台打印的中文乱码不是代码生成的问题而是 Maven 运行时默认编码不对。在pom.xml里给插件增加encoding配置configuration configurationFilesrc/main/resources/generatorConfig.xml/configurationFile overwritetrue/overwrite verbosetrue/verbose /configuration编码问题建议在 JVM 层面解决设置MAVEN_OPTS-Dfile.encodingUTF-8。如果仍不奏效在配置文件顶部加上property namejavaEncoding valueUTF-8/放在generatorConfiguration的properties之后。5.2 生成的实体类没有注释同事抱怨默认关闭注释团队协作时对代码可读性要求高的话可以通过自定义注释生成器来加上中文注释。做法是实现org.mybatis.generator.api.CommentGenerator接口重写addModelClassComment、addFieldComment等方法再从数据库的Remark元数据中读取字段注释注入到生成代码里。实现完成后在commentGenerator typecom.example.CommentGenerator/中指定。要注意的是自定义类必须在 MBG 运行的 classpath 中——Maven 插件方式下直接将类放在项目源码中即可被加载命令行方式时需要把编译后的 class 路径加入--classpath参数。5.3 重复生成文件手工修改被覆盖MBG 默认对已存在文件有保留策略执行生成时如果某个文件已存在且内容与将要生成的内容一致或 MBG 检测到文件不是自己生成的会选择不覆盖。但插件配置里如果显式设置overwritetrue/overwrite则所有被生成的文件都会无条件覆盖。我强调一个团队纪律MBG 生成的文件不要手工修改需要调整的地方要么改表结构要么改配置要么在生成后用脚本统一替换。一旦手工改了下次生成就会丢改动的逻辑而且这种丢失很难通过 Git 记录察觉。5.4 只生成了一部分表日志里没有错误这种情况多半是tableName的匹配问题MySQL 表名大小写在 Linux 下敏感而 Windows 下不敏感。如果你的库表名是Sys_User配置里写sys_user在 Linux 上跑 Maven 插件就可能匹配不到表但又不报错。解决方案是严格照搬数据库里的实际表名如果库表命名比较规范可以使用%匹配做快速生成但生成前必须自己过滤掉不需要的表。5.5 targetRuntime 选错导致生成代码风格诡异选MyBatis3DynamicSql或更早版本遗留的其他运行时可能生成大量Generated等注解和构造器风格的类。团队项目中对生成代码风格有要求时最好锁定MyBatis3Simple或MyBatis3这两个选项并在项目 README 中注明原因。换运行时后一定要删掉旧生成的文件再重新生成因为 MBG 对既有文件的保留策略可能让新旧代码混在一起。6. 进阶结合 Lombok、自定义模板与多环境配置6.1 去掉 Setter/Getter生成 Lombok 风格的实体实体类字段多时产出的上千行 getter/setter 非常占篇幅而且每次表结构变更重新生成代码代码 diff 会特别大。我实际项目中的做法是通过自定义插件在生成实体类时自动添加Data注解并抑制所有 getter/setter 的生成。核心思路是继承PluginAdapter在modelBaseRecordClassGenerated方法中向生成的类添加注解再重写modelSetterMethodGenerated和modelGetterMethodGenerated返回false。public class LombokPlugin extends PluginAdapter { Override public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { topLevelClass.addImportedType(lombok.Data); topLevelClass.addAnnotation(Data); return true; } Override public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) { return false; } // getter 同理返回 false }配置中注册插件plugin typecom.example.LombokPlugin/这里提醒一点如果项目原先已经生成了大量带 getter/setter 的旧实体类启用 Lombok 插件后必须全量重新生成并检查编译是否引用了那些“被删掉”的方法——虽然 Lombok 编译后会补全但 IDE 的即时编译在某些环境下会缓存旧结构建议 clean 后再编译。6.2 多环境配置开发、测试、生产用同一套模板数据库在不同环境下的地址、用户名不同我们的配置模板通过generator.properties隔离了这些差异。推荐的做法是维护三份 properties 文件generator-dev.properties、generator-test.properties、generator-prod.properties。在 Maven 插件配置里通过properties指定当前激活的环境参数或者在命令行执行mvn mybatis-generator:generate -Dmybatis.generator.properties.filesrc/main/resources/generator-dev.properties这种方式最能避免无意中在生成代码时连上生产库。我亲眼见过生产事故模拟项目正是因为开发机的配置文件里写了生产库连接地址凌晨跑定时任务时把生产表结构读了一遍导致部分运行时元数据被缓存——虽然没改数据但足够让人后怕。配置文件绑定环境后这种行为能被有效阻断。6.3 生成代码的分层依赖MBG 生成的三类文件之间天然存在依赖关系实体类不依赖其他生成文件Mapper 接口依赖实体类XML 映射文件中的resultMap与parameterType都依赖实体类全限定名。如果调整了targetPackage务必保持三者之间的包路径彼此协调。举例来说com.example.model里的实体类如果改到com.example.pojo那么 XML 文件中的parameterTypecom.example.model.SysUser也得全部替换。有的团队干脆在生成后执行一段全局文本替换脚本将旧包名批量替换成新包名这种方法可行但维护成本更高我更推荐一步到位直接更新生成配置和模板源从源头保证一致性。7. 命令行运行与常见参数说明除了 Maven 插件外MBG 也支持命令行运行。命令行方式适合在没有完整 Maven 环境的机器上使用比如一些内网部署机。运行命令如下java -jar mybatis-generator-core-1.4.2.jar \ -configfile generatorConfig.xml \ -overwrite-configfile指定 XML 配置文件路径-overwrite表示覆盖已存在的同名文件。还有一个-verbose参数打印生成过程的详细日志排查问题时建议加上。如果配置文件中引用了外部 properties 文件而 MBG 无法在当前目录找到它就会报Cannot load resource ...错误。命令行启动时的工作目录就是当前目录所以建议把 properties 文件与 XML 放在同一目录或者使用绝对路径。依赖 JDBC 驱动时命令行需要把驱动 jar 和自定义插件 jar 都通过-classpath参数显式指定例如java -cp mybatis-generator-core-1.4.2.jar:mysql-connector-java-8.0.33.jar \ org.mybatis.generator.api.ShellRunner \ -configfile generatorConfig.xml -overwrite -verboseWindows 环境下路径分隔符用分号而非冒号这一点常有人踩坑。我建议团队统一使用 Maven 插件方式因为依赖管理、编码、classpath 都更好控制命令行方式定位为“应急手段”。8. 配置模板维护的团队协作建议8.1 版本控制与评审配置文件需要纳入版本控制而且应当指定专人维护。它不像普通代码那样频繁变更但每次变更影响面都很大——一次修改会导致全项目范围内的生成代码全部变化。我经手的项目里配置文件改动必须经过至少一位熟悉生成机制的同事评审后才允许合并。评审重点不是格式而是是否修改了目标包路径是否更改了运行时是否关闭了某些表这些都属于“牵一发动全身”的变更。8.2 生成代码的提交策略很多团队把 MBG 生成的代码直接提交到 Git 仓库。我认可这种做法因为它能保证其他成员克隆后无需任何额外生成步骤即可编译运行。但前提是必须有“生成后 diff 审核”的习惯执行生成命令后git diff查看变更内容确认与表结构变更一致后再提交。没有这个习惯代码库里会出现大量“幽灵改动”——生成时间不同、驱动版本不同、MBG 版本不同都可能导致细微差异最终合并的时候痛苦的是所有人。8.3 与数据库变更的联动MBG 只是“单向同步”从表到代码。表结构改了需要手动重新运行生成命令。比较理想的做法是在数据库迁移脚本比如 Flyway中调用生成命令或至少把生成命令写入 CI 流水线的一个手动触发任务里。这样能强制每人在表结构变更后都看到最新代码而不是等同事手动跑一遍再传代码。我使用过的一个项目在数据库迁移工具的 afterMigrate 钩子里自动执行 MBG 生成也会把 Maven 插件绑定到generate-sources阶段让它成为 Maven 编译的固定环节。这种做法极大程度减少了“代码与表结构不一致”的问题。8.4 我的经验心得写到这里分享一个在实际项目中反复验证过的经验MBG 配置文件的第一版宁可多花半小时精雕细琢也不要图快直接复制网上模板。原因很简单这个文件是一个高频“生产资料”每一次生成都在消耗它。如果它一开始就是整洁、合理、有注释的后面所有生成的代码都会在这个轨道上运行。我第一次使用 MBG 时直接在网上下载了一份包含大量冗余节点的模板因为对每个节点理解不深出现问题无法定位消耗了数倍时间排错。另一个教训是生成代码之前务必确认数据库连接账号不被其他任务占用。MBG 会读取表结构的元数据如果同时有长事务在更新表结构可能因为锁等待问题导致生成超时。这个场景不多见但一旦出现排查成本极高。我通常的做法是在低峰期执行生成或者准备一个只读账号专门用于生成工具连接。9. 写在最后的小建议如果你把 MBG 当作“一次性代码生成工具”用完就忘那遇到问题是迟早的事如果你把它当作一套“代码生产流水线”认真维护配置文件那么后续的每一次表结构变更、字段新增、索引调整都会变得非常顺畅。我个人在实际使用中更喜欢让项目始终保持“配置驱动生成”的姿势表结构变更的意思是先改表再改配置再跑命令最后提交生成代码。这四个动作顺序基本固定少一步都会出问题。最后再分享一个小技巧在tableName中不建议使用%通配符直接生成全库表除非你确认所有表都要上线。但如果你确实需要快速生成一批表可以在生成后运行一段简单的文件清理脚本将不再是业务表的文件移出目录。这比在配置里反复增删table节点更省心。用好 MBG 不在于跑通一次而在于掌握生成逻辑之后把它融入日常开发节奏让它持续替你节省重复劳动。