MyBatis-Generator 配置详解:从零到一生成高质量代码

发布时间:2026/10/15 16:24:44
MyBatis-Generator 配置详解:从零到一生成高质量代码 1. 为什么还要聊 MyBatis 逆向工具如果你写过 Java 后端尤其是维护过那种表结构几十张、字段上百个的老系统一定经历过这种场景产品经理丢过来一个新需求你打开数据库客户端对着建表语句一行行抄字段名然后在 IDE 里手写实体类、Mapper 接口、XML 映射文件。抄到第十个字段的时候眼睛已经开始花了create_time写成creat_timeis_deleted的映射类型写成String编译能过跑起来一查数据全是 null。这种重复劳动MyBatis-Generator 就是专门来终结它的。它的核心逻辑非常朴素读取数据库的元数据表名、字段名、字段类型、注释按照你配置的规则自动生成 Java 实体类、Mapper 接口和 XML 映射文件。你只需要改配置文件里的几个参数剩下的交给它批量产出。我见过不少团队对这类工具的态度两极分化。一派觉得生成的代码太丑不如手写可控另一派则把它当成项目脚手架新模块一上来先跑一遍生成再在生成结果上做二次加工。我的经验是关键在于你怎么配置它而不是它本身好不好用。配置得当生成出来的代码可以直接进版本库配置随意那确实是一堆需要大改的垃圾。这篇文章面向的是刚接触 MyBatis-Generator、或者用过但总觉得生成结果不顺手的朋友。我会从配置文件的每一个关键参数讲起把生成策略、命名规则、类型映射这些容易踩坑的地方拆开说清楚最后给出一套我自己在用的、可以直接抄的配置模板。全程不涉及任何特定公司的内部框架就是最通用的 MyBatis-Generator 用法。2. 先把 generatorConfig.xml 的骨架搭明白MyBatis-Generator 的运行方式有好几种Maven 插件、Java 代码调用、命令行。不管哪种方式核心都是同一个配置文件通常叫generatorConfig.xml。这个文件的结构其实就三块数据库连接配置、生成策略配置、表清单配置。很多人一上来就复制网上的模板改个数据库密码就跑结果生成出来的包名不对、路径不对、注释乱码然后又回头一点点试。与其这样不如先把每一块的作用搞清楚。2.1 数据库连接与驱动加载的细节配置文件最上面是jdbcConnection节点这里有几个参数值得单独说jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/demo_db?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai userIdroot passwordyour_password /jdbcConnectiondriverClass这块MySQL 8.x 之后驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver如果你还用老的名字控制台会给你一个警告虽然不一定报错但建议直接换成新的。connectionURL里的serverTimezone参数是很多人忽略的坑不配的话生成的实体类里Date类型字段在运行时会因为时区问题差几个小时排查起来很费劲。还有一个隐藏问题驱动 jar 包不在 classpath 里。用 Maven 插件方式运行时插件依赖和项目依赖是分开的你项目里引了 MySQL 驱动不代表插件能加载到。稳妥的做法是在插件的dependencies里显式声明驱动依赖版本和你项目保持一致。提示如果数据库密码里有特殊字符比如、#在 XML 里需要做转义或者用 CDATA 包起来否则解析会出错。2.2 生成策略配置决定了代码长什么样javaModelGenerator、sqlMapGenerator、javaClientGenerator这三个节点分别控制实体类、XML 文件、Mapper 接口的生成位置和包名。它们的targetPackage和targetProject两个属性必须配合好targetProject是相对于工作目录的路径targetPackage是包名工具会自动把包名转成目录结构。javaModelGenerator targetPackagecom.example.demo.entity targetProjectsrc/main/java property nameenableSubPackages valuetrue/ property nametrimStrings valuetrue/ /javaModelGeneratorenableSubPackages这个属性当你的表名带前缀比如t_user、t_order时特别有用。开启后工具会根据表名的前缀自动生成子包t_user会进到entity.t包下。但说实话这个行为大多数人不喜欢因为包名会变得很奇怪。我的建议是关掉它用后面的domainObjectRenamingRule来统一去掉表前缀这样包结构更干净。trimStrings属性控制是否对字符串类型的字段做 trim 处理默认是 false。如果你的表里有char类型字段建议开启避免查询出来的值带一堆空格。2.3 表清单配置的两种写法表清单有两种配置方式table逐个指定或者用table的tableName属性配合通配符。逐个指定适合只生成部分表的场景通配符适合全库生成。table tableNamet_user domainObjectNameUser enableCountByExamplefalse enableUpdateByExamplefalse enableDeleteByExamplefalse enableSelectByExamplefalse selectByExampleQueryIdfalse /table这里有一组enableXxxByExample属性默认全是 true。这意味着每张表会额外生成一堆XxxExample类和相关方法。对于简单的 CRUD 场景这些 Example 类基本用不上反而让代码库变得臃肿。我的习惯是全部关掉只保留基础的增删改查方法。如果你的项目确实需要动态条件查询再单独打开。3. 让生成的代码能直接用的几个关键配置配置文件跑通只是第一步生成出来的代码能不能直接用取决于你对命名规则、类型映射、注释生成这几个方面的调教。这一块是区分能用和好用的分水岭。3.1 表名前缀与字段前缀的批量处理实际项目里表名带前缀是常态t_user、tb_order、sys_role。如果直接生成实体类名就是TUser、TbOrder、SysRole看着别扭。这时候需要用到domainObjectRenamingRuletable tableNamet_% domainObjectName enableCountByExamplefalse enableUpdateByExamplefalse enableDeleteByExamplefalse enableSelectByExamplefalse selectByExampleQueryIdfalse domainObjectRenamingRule searchString^T replaceString/ /tablesearchString是正则表达式replaceString是替换后的内容。上面这个配置会把t_user生成的类名从TUser变成User。注意这个规则作用于生成的类名不是表名本身。字段前缀的处理类似用columnRenamingRulecolumnRenamingRule searchString^is_ replaceString/这样is_deleted字段生成的属性名就是deleted而不是isDeleted。不过这里要小心如果去掉前缀后和 Java 关键字冲突比如class、new生成会失败需要手动调整。3.2 字段类型映射的覆盖与自定义MyBatis-Generator 内置了一套 JDBC 类型到 Java 类型的映射表大部分情况下够用。但有两种情况需要你手动干预第一种是tinyint(1)。MySQL 里很多人用它当布尔值但生成器默认会映射成Byte或Integer而不是Boolean。解决办法是在javaTypeResolver里配置javaTypeResolver property nameforceBigDecimals valuefalse/ property nameuseJSR310Types valuetrue/ /javaTypeResolveruseJSR310Types设为 true 后datetime会映射成LocalDateTimedate映射成LocalDate比老的java.util.Date好用得多。至于tinyint(1)转 Boolean需要自定义 TypeResolver继承JavaTypeResolverDefaultImpl并重写calculateJavaType方法这个稍微有点门槛但网上有现成的实现可以借鉴。第二种是decimal类型。默认会映射成BigDecimal这是对的。但如果你把forceBigDecimals设成 true所有numeric类型都会强制转成BigDecimal包括int这显然不是你想要的。所以这个属性保持 false 就好。3.3 注释生成别让数据库注释白写数据库建表时写的COMMENT如果生成器不读那就白写了。开启注释生成很简单在commentGenerator节点里配置commentGenerator property nameaddRemarkComments valuetrue/ property namesuppressDate valuetrue/ property namesuppressAllComments valuefalse/ /commentGeneratoraddRemarkComments设为 true 后数据库里的字段注释会变成实体类字段上的 Javadoc。suppressDate建议设为 true否则每次生成都会在注释里加时间戳导致 Git diff 里全是无意义的变更。suppressAllComments如果设成 true那前面两个配置就都失效了所以保持 false。这里有个细节注释里的换行符和特殊字符。如果数据库注释里写了多行文本生成到 Javadoc 里可能会破坏格式。稳妥的做法是建表时注释保持单行或者用自定义的 CommentGenerator 做转义处理。4. 跑通生成之后的二次加工与维护策略生成代码只是起点真正的工作量在于生成之后怎么管理这些代码。我见过两种极端一种是每次表结构变更就重新生成覆盖掉所有手工修改另一种是把生成结果复制出来改个名字从此和生成器说再见。这两种都有问题。4.1 生成代码与手写代码的边界划分我的做法是分层管理生成的实体类和 Mapper 接口放在generated包下手写的业务逻辑放在manual包下。Mapper 接口继承生成的接口业务方法写在子接口里。这样表结构变更时重新生成generated包不会影响手写代码。// generated 包下自动生成 public interface UserMapper { int deleteByPrimaryKey(Long id); int insert(User record); User selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(User record); } // manual 包下手写扩展 public interface UserMapperExt extends UserMapper { ListUser selectByCondition(UserQuery query); int batchInsert(ListUser users); }XML 文件同理生成的 XML 只包含基础 SQL手写的 XML 单独一个文件通过mapper namespace...UserMapperExt关联。这样职责清晰重新生成时不会误伤。4.2 表结构变更后的增量生成表结构变更后不要全库重新生成那样会产生大量无意义的 diff。正确的做法是只生成变更的表。把table节点改成只包含目标表跑一次生成然后检查 diff确认无误后提交。如果变更只是加了一个字段生成器会在实体类里加一个属性在 XML 里加对应的resultMap映射和insert/update语句片段。这些变更通常是安全的。但如果字段类型变了比如从varchar改成int生成器会改属性类型这时候要检查所有引用该属性的手写代码避免编译错误。注意重新生成前一定要先提交当前工作区的代码或者用git stash保存。生成器覆盖文件是不可逆的没有版本控制兜底很容易出事。4.3 把生成器集成到构建流程里如果团队里多个人协作建议把生成器配置和运行方式固化下来。Maven 插件方式最省事plugin groupIdorg.mybatis.generator/groupId artifactIdmybatis-generator-maven-plugin/artifactId version1.4.2/version configuration configurationFilesrc/main/resources/generatorConfig.xml/configurationFile overwritetrue/overwrite verbosetrue/verbose /configuration /plugin配置好后运行mvn mybatis-generator:generate就能生成。overwrite设为 true 表示覆盖已有文件verbose设为 true 会在控制台输出详细日志方便排查问题。但这里有个团队协作的坑不同人本地的数据库表结构可能不一致。如果 A 同学的表里多了一个字段B 同学跑生成器时就会把这个字段的映射删掉。解决办法是让生成器连接一个统一的开发库或者把生成器运行作为 CI 流程的一部分由专人负责。5. 那些年我踩过的生成器坑说了这么多配置最后聊几个实际踩过的坑都是文档里不会写、但遇到了很耽误时间的。5.1 生成的文件编码不对导致中文注释乱码这个问题在 Windows 环境下特别常见。生成器默认用系统编码读取和写入文件如果数据库是 UTF-8系统是 GBK生成的 Java 文件里中文注释就是乱码。解决办法是在 Maven 插件配置里加 JVM 参数configuration configurationFilesrc/main/resources/generatorConfig.xml/configurationFile overwritetrue/overwrite /configuration然后在MAVEN_OPTS里设置-Dfile.encodingUTF-8。或者更彻底一点在commentGenerator里配置javaFileEncoding属性为UTF-8。这个属性不是所有版本都支持1.4.x 之后才比较稳定。5.2 主键类型识别错误如果表的主键是bigint unsigned生成器有时会识别成BigInteger而不是Long。这会导致 Mapper 接口里的selectByPrimaryKey参数类型不对调用时编译报错。解决办法是在table节点里显式指定主键列table tableNamet_user domainObjectNameUser generatedKey columnid sqlStatementMySql identitytrue/ /tablegeneratedKey节点告诉生成器主键列是自增的生成的insert语句会带上useGeneratedKeys配置实体类的主键属性也会被正确标注。5.3 生成器把视图也当成表生成了如果数据库里有视图用通配符tableName%时生成器会把视图也扫出来生成一堆无意义的实体类。解决办法是用tableName精确指定表或者在配置里加schema过滤。有些版本的生成器支持property nameignoreQualifiersAtRuntime valuetrue/但最稳妥的还是手动列出需要生成的表。5.4 重复生成导致 XML 里的 resultMap 冲突如果手写的 XML 里已经定义了同名的resultMap重新生成时会产生冲突MyBatis 启动时报 Result Maps collection already contains value 错误。解决办法是生成的 XML 和手写的 XML 用不同的 namespace或者手写 XML 里不要定义和生成文件同名的 resultMap。6. 一套可以直接抄的配置模板说了这么多理论最后给一份我目前在用的配置模板。这份配置的特点是关闭了所有 Example 类生成、开启了 JSR310 时间类型、去掉了表前缀、开启了数据库注释、生成的代码可以直接进版本库。?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 context idmysql targetRuntimeMyBatis3 defaultModelTypeflat property namejavaFileEncoding valueUTF-8/ property namebeginningDelimiter value/ property nameendingDelimiter value/ commentGenerator property nameaddRemarkComments valuetrue/ property namesuppressDate valuetrue/ property namesuppressAllComments valuefalse/ /commentGenerator jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/demo_db?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai userIdroot passwordyour_password /jdbcConnection javaTypeResolver property nameforceBigDecimals valuefalse/ property nameuseJSR310Types valuetrue/ /javaTypeResolver javaModelGenerator targetPackagecom.example.demo.generated.entity targetProjectsrc/main/java property nameenableSubPackages valuefalse/ property nametrimStrings valuetrue/ /javaModelGenerator sqlMapGenerator targetPackagecom.example.demo.generated.mapper targetProjectsrc/main/resources property nameenableSubPackages valuefalse/ /sqlMapGenerator javaClientGenerator typeXMLMAPPER targetPackagecom.example.demo.generated.mapper targetProjectsrc/main/java property nameenableSubPackages valuefalse/ /javaClientGenerator table tableNamet_user domainObjectNameUser enableCountByExamplefalse enableUpdateByExamplefalse enableDeleteByExamplefalse enableSelectByExamplefalse selectByExampleQueryIdfalse generatedKey columnid sqlStatementMySql identitytrue/ domainObjectRenamingRule searchString^T replaceString/ /table /context /generatorConfiguration这份配置里defaultModelType设为flat表示每个表生成一个实体类不生成带主键的独立类。beginningDelimiter和endingDelimiter设为反引号是为了兼容 MySQL 里可能出现的保留字字段名。javaClientGenerator的type设为XMLMAPPER表示 Mapper 接口和 XML 文件分离这也是目前最主流的用法。用这份配置跑一遍生成的实体类字段名干净、注释完整、时间类型是LocalDateTimeMapper 接口只有基础的增删改查方法。后续要加自定义查询在manual包里扩展就行。7. 关于生成器的一些个人体会用了这么多年 MyBatis-Generator我最大的感受是它解决的是从零到一的问题不是从一到一百的问题。表结构刚建好、项目刚起步的时候它能帮你省下大量机械劳动但项目进入维护期后表结构变更频繁每次重新生成都要小心翼翼检查 diff这时候它的价值就下降了。我的建议是新项目、新模块大胆用生成器打底老项目、核心模块手写为主生成器只用来做参考。另外生成器的配置最好纳入版本控制团队统一使用同一份配置避免每个人生成出来的代码风格不一致。还有一点不要过度依赖生成器的 Example 查询。那套 API 虽然灵活但可读性差复杂查询写出来像天书。我见过一个项目里一个多表关联查询用 Example 拼了上百行代码后来维护的人直接重写成了手写 SQL。生成器给的是基础能力复杂逻辑还是得靠人。最后分享一个小技巧如果生成出来的实体类字段太多看着眼花可以在 IDE 里装一个折叠插件的把 getter/setter 折叠起来只看字段定义。或者用 Lombok 的Data注解替代 getter/setter生成器虽然不直接支持 Lombok但可以在生成后手动加注解或者用自定义的插件模板。这个就看个人取舍了Lombok 能减少代码量但也会带来一些编译期依赖的问题团队里要统一意见。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询