ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MyBatis Generator 配置实战:从建表到生成代码的完整指南

MyBatis Generator 配置实战:从建表到生成代码的完整指南 1. 为什么还要自己写Mapper先聊聊生成器的价值边界先抛一个结论mybatis-generator这玩意儿不是让你彻底不写SQL而是把最没有技术含量的那部分代码从你的工作量里抹掉。我在实际项目里见过太多这样的场景一个订单表二三十个字段手写insert、update、selectByPrimaryKey每个方法都是机械重复。字段多了还容易漏漏一个字段运行时才报错查半天发现是某个字段没写进映射。更尴尬的是同事的代码风格各不相同张三的Mapper里缩进用四个空格李四的用两个Tab代码评审的时候一半时间花在争论格式上。上面这些痛点恰好都是mybatis-generator下文简称MBG能解决的。MBG是MyBatis官方出品的老牌代码生成器从数据库表结构反向生成三层东西实体类Model、Mapper接口、XML映射文件。你用一条命令它就能把一张表对应的全套CRUD代码给你吐出来。如果你用的是MySQL把表结构设计好之后生成代码几乎是零成本的事情。这篇文章写给谁三种人刚开始接触Spring Boot MyBatis还在手动写增删改查的新手项目里已经有不少表想统一代码风格、提高开发效率的团队听别人提过代码生成器但没系统搞明白配置和坑点的人2021版的MBG在配置和依赖上跟老版本有一些差异网上很多教程都是基于1.3.x的老写法直接拷贝过来会报错。这篇文章我会基于实际踩过的坑从零到一把整个流程梳理清楚。先说清楚边界MBG适合生成单表的基础CRUD它不擅长处理复杂的多表关联查询、动态SQL里那些业务味很重的逻辑。这类场景仍然需要你自己手写。所以正确的预期是——用生成器把80%的重复代码搞定剩下20%的复杂查询自己在XML里补。2. 准备工作版本选择、依赖引入与工程结构2.1 2021年这个时间点版本怎么选MBG的版本演进其实有一个比较大的分水岭1.4.0版本对包结构做了调整。1.3.x时代的包名是org.mybatis.generator1.4.0之后核心类迁移到了org.mybatis.generator.api下面的新包结构同时指定了Java版本要求。2021年写新项目我建议直接用1.4.0以上的版本Maven仓库里最新的稳定版是1.4.0之后还有1.4.1、1.4.2的小版本修复。版本这块有个很容易踩的坑你拿网上旧教程的配置去跑1.4.x会发现某些配置节点的属性变了、某些类已经废弃了报错信息还很隐晦根本看不出是版本兼容问题。我个人的建议组合是这样组件推荐版本说明JDK1.8及以上MBG 1.4.0之后强制要求JDK 1.8mybatis-generator-core1.4.0或1.4.2核心生成引擎mybatis-generator-maven-plugin1.4.0或1.4.2Maven插件方式运行mybatis3.5.x运行时依赖MBG本身不强依赖具体版本如果你用的是Spring Boot 2.4.x或2.5.x内置的MyBatis Starter版本通常已经够用不需要额外操心。2.2 两种项目接入方式推荐Maven插件MBG的运行方式主要有三种命令行、Maven插件、Java代码直接调用。2021年最主流、最推荐的方式就是Maven插件方式因为它是声明式的配置文件放在项目里团队共享任何人clone下来执行一条mvn mybatis-generator:generate就能复现同样的生成结果。命令行方式适合不依赖构建工具的临时场景Java代码方式适合把生成能力封装成自己的可视化工具。这两个后面会展开讲。先看工程目录结构。假设一个标准的Spring Boot多模块或单模块项目建议这样组织project-root/ ├── pom.xml ├── src/main/java/com/example/demo/ │ ├── entity/ // 实体类生成的Model放这里 │ ├── mapper/ // Mapper接口 │ └── resources/ │ ├── mapper/ // XML映射文件 │ └── generator/ // MBG配置文件单独放一个目录 └── sql/ // 建表SQL脚本配置文件generatorConfig.xml放在src/main/resources/generator下不要扔在src根目录跟application.yml混在一起后面维护的时候好找。2.3 pom.xml里需要加的东西用Maven插件方式只需要在pom.xml里引入插件。核心片段如下build plugins plugin groupIdorg.mybatis.generator/groupId artifactIdmybatis-generator-maven-plugin/artifactId version1.4.0/version configuration !-- 指定配置文件路径 -- configurationFile${basedir}/src/main/resources/generator/generatorConfig.xml/configurationFile !-- 允许覆盖已有文件默认false如果体验生成功能建议先false -- overwritetrue/overwrite verbosetrue/verbose /configuration dependencies !-- 数据库驱动这里以MySQL为例 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.25/version /dependency /dependencies /plugin /plugins /build有两个细节要特别提醒第一个数据库驱动一定要在插件的dependencies里声明。很多人只在自己的工程依赖里加了JDBC驱动结果跑插件时提示ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因是Maven插件运行在独立的ClassLoader里跟主工程的依赖不共享必须在插件内部单独声明。第二个overwrite这个参数。设置为true时每次生成会覆盖同名文件设置为false时遇到已存在的文件会跳过。我建议开发初期设为false确认生成结果满意之后再用true防止手滑把手工改过的代码覆盖了。3. generatorConfig.xml核心配置逐项拆解3.1 配置文件的结构总览MBG的配置文件核心是generatorConfiguration根节点里面通常包含这么几块?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 !-- 1. 可选的properties文件用于外部化配置 -- properties resourcegenerator/config.properties/ !-- 2. context节点一个context对应一套生成策略 -- context idmysqlContext targetRuntimeMyBatis3Simple defaultModelTypeflat !-- 3. 注释生成策略 -- commentGenerator property namesuppressAllComments valuetrue/ /commentGenerator !-- 4. 数据库连接 -- jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/demo_db?useSSLfalseamp;serverTimezoneUTC userIdroot password123456/ !-- 5. 实体类生成策略 -- javaModelGenerator targetPackagecom.example.demo.entity targetProjectsrc/main/java property nametrimStrings valuetrue/ /javaModelGenerator !-- 6. SQL映射文件生成策略 -- sqlMapGenerator targetPackagemapper targetProjectsrc/main/resources/ !-- 7. Mapper接口生成策略 -- javaClientGenerator typeXMLMAPPER targetPackagecom.example.demo.mapper targetProjectsrc/main/java/ !-- 8. 表配置 -- table tableNameuser domainObjectNameUser/ /context /generatorConfiguration上面这个配置是我精简过的核心版本实际项目还可能配置plugins、table的列覆盖、日期类型转换规则等。下面逐个节点说明作用和容易被忽略的点。3.2 连接信息连接URL里的时区问题jdbcConnection节点没什么玄机就是数据库连接信息。但这里有个高频报错我几乎每次在群里都能看到The server time zone value йʱ is unrecognized or represents more than one time zone.这是MySQL 8.x驱动引入的严格时区校验导致的。老教程里用的是jdbc:mysql://localhost:3306/demo_db这种不带参数的写法放到MySQL 8 JDBC 8驱动下直接报时区错误。解决办法是连接URL后面拼接参数jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/demo_db?useSSLfalseamp;serverTimezoneAsia/Shanghai userIdroot password123456/两个参数说明useSSLfalse本地开发环境没必要走SSL握手加上能减少连接耗时同时避免MySQL 8默认开SSL导致的警告日志。serverTimezoneAsia/Shanghai显式告诉驱动使用东八区避免服务器时区和驱动默认时区不一致导致的乱码或时间偏移。还有一个细节XML里符号必须转义成amp;这是XML语法的硬性要求不转义的话解析会直接失败。3.3 生成路径targetProject的三种写法javaModelGenerator和sqlMapGenerator都涉及targetPackage和targetProject两个属性很多人一开始搞不清这两个属性的配合规则。targetPackage指定的是包名或者目录名targetProject指定的是相对于当前执行模块的路径。实际使用中有三种典型写法写法一Maven工程生成到src/main/javajavaModelGenerator targetPackagecom.example.demo.entity targetProjectsrc/main/java这种写法直接生成到源码目录生成完就能编译是我最推荐的方式。写法二Maven工程生成到独立的source目录javaModelGenerator targetPackagecom.example.demo.entity targetProjectsrc/main/gensrc/main/gen是独立的生成目录需要在pom.xml里把src/main/gen配置成build-helper-maven-plugin的额外source目录。好处是生成代码和手写代码物理隔离重新生成的时候不会污染你的手工代码。写法三绝对路径javaModelGenerator targetPackagecom.example.demo.entity targetProjectD:/workspace/demo/src/main/java不推荐换个环境路径就失效了配置文件失去可移植性。XML映射文件的写法略微不同sqlMapGenerator targetPackagemapper targetProjectsrc/main/resources/这里的targetPackage填的是mapper生成后对应src/main/resources/mapper/目录。如果你的XML文件规约放在resources/mapper下这样写即可。3.4 table节点指定表名、实体名和列覆盖table节点是核心中的核心它决定了对哪张表生成代码。最简单的写法table tableNameuser/此时MBG默认用表名作为实体名user表生成User.java同时Mapper接口叫UserMapperXML叫UserMapper.xml。如果有特殊命名比如表名带前缀t_user你不想让实体类叫TUser可以显式指定table tableNamet_user domainObjectNameUser/domainObjectName指定实体类名MBG会自动推导出UserMapper、UserMapper.xml这些衍生文件名。还有个enableInsert、enableSelectByPrimaryKey之类的开关用来控制单表生成的CRUD方法类别。默认全开但有些表没必要生成insert比如配置表只读可以这么写table tableNameconfig domainObjectNameConfig enableInsertfalse enableUpdateByPrimaryKeyfalse enableDeleteByPrimaryKeyfalse/保留selectByPrimaryKey和selectAll就够了。3.5 注释生成策略把默认注释关掉的关键作用MBG默认会在生成的每个类、每个方法上生成一段注释格式大致是/** * 表名user * 表注释用户表 * author MyBatis Generator * date 2021-08-15 */这段注释最大的问题在于——它带生成时间。如果你启用了版本管理每次重新生成代码即使表结构没变生成文件的注释时间变了Git里就会多出一堆无意义的diff。代码评审的时候很容易被追问这次改动改了什么答重新跑了一次生成器。解决方案就是commentGenerator里关掉默认注释commentGenerator property namesuppressAllComments valuetrue/ /commentGeneratorsuppressAllComments设为true后所有自动注释直接不生成干净利落。如果你还是想保留一些业务含义可以自定义注释生成器继承DefaultCommentGenerator重写方法设置addRemarkComments属性这个后面进阶部分展开。4. 跑通整个流程从建表到生成代码的完整实操4.1 准备一张测试表为了演示一个完整流程我先建一张订单表CREATE TABLE t_order ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, order_no varchar(64) NOT NULL COMMENT 订单编号, user_id bigint(20) NOT NULL COMMENT 下单用户ID, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 订单状态0-待支付 1-已支付 2-已取消, remark varchar(512) DEFAULT NULL COMMENT 备注, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;这张表包含了自增主键、普通索引、DEFAULT值、ON UPDATE CURRENT_TIMESTAMP涵盖了比较常见的MySQL表特性。4.2 完整配置示例针对这张表完整的generatorConfig.xml是这样的?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 idmysqlContext targetRuntimeMyBatis3Simple defaultModelTypeflat commentGenerator property namesuppressAllComments valuetrue/ /commentGenerator jdbcConnection driverClasscom.mysql.cj.jdbc.Driver connectionURLjdbc:mysql://localhost:3306/demo_db?useSSLfalseamp;serverTimezoneAsia/Shanghai userIdroot password123456/ javaModelGenerator targetPackagecom.example.demo.entity targetProjectsrc/main/java property nametrimStrings valuetrue/ /javaModelGenerator sqlMapGenerator targetPackagemapper targetProjectsrc/main/resources/ javaClientGenerator typeXMLMAPPER targetPackagecom.example.demo.mapper targetProjectsrc/main/java/ table tableNamet_order domainObjectNameOrder/ /context /generatorConfiguration这里说明两个关键选型targetRuntimeMyBatis3Simple这个选项很关键它生成的方法数量远少于默认的MyBatis3只生成countByExample、deleteByPrimaryKey、insert、selectByPrimaryKey、updateByPrimaryKey、selectAll这些基本方法不生成基于Example的复杂查询方法。实际项目中大部分单表操作用Simple就够了代码量少、可读性强。defaultModelTypeflat这个选项让每张表只生成一个实体类不会再拆出Key类、Criteria类。默认的conditional模式遇到联合主键会额外生成复合主键类徒增复杂度。没有特殊需求就用flat。4.3 执行生成命令在项目根目录执行mvn mybatis-generator:generate如果你在pom.xml里有多个插件配置、或者有多个profile可以指定mvn -Pdev mybatis-generator:generate -Dmybatis.generator.configurationFilesrc/main/resources/generator/generatorConfig.xml首次执行建议加上-X参数打印完整调试信息mvn mybatis-generator:generate -X这样如果配置有问题你能看到完整的异常堆栈而不是被Maven吞掉只有一行报错的提示。生成成功后控制台输出类似[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.257 s [INFO] ------------------------------------------------------------------------4.4 生成结果长什么样生成完成后目录结构如下src/main/java/com/example/demo/ ├── entity/ │ └── Order.java └── mapper/ └── OrderMapper.java src/main/resources/mapper/ └── OrderMapper.xml看一眼实体类的核心代码package com.example.demo.entity; import java.math.BigDecimal; import java.util.Date; public class Order { private Long id; private String orderNo; private Long userId; private BigDecimal totalAmount; private Integer status; private String remark; private Date createdAt; private Date updatedAt; // 省略getter/setter }注意几点decimal(10,2)映射成BigDecimaldatetime映射成Datetinyint映射成Integerbigint映射成Long。这是JDBC驱动的默认映射规则合理且符合直觉。再看Mapper接口package com.example.demo.mapper; import com.example.demo.entity.Order; public interface OrderMapper { int deleteByPrimaryKey(Long id); int insert(Order row); Order selectByPrimaryKey(Long id); java.util.ListOrder selectAll(); int updateByPrimaryKey(Order row); }干净利落。insert和updateByPrimaryKey默认带入所有字段包括null字段如果你希望只操作非空字段后面还有insertSelective和updateByPrimaryKeySelective可以通过table节点里的insertStatementSupportsSelectKey或切换到MyBatis3运行时获得。XML文件的内容不必全贴核心结构是有ResultMap、各个SQL语句的完整映射。重点说一个我在实际项目里几乎每次都要改的点updateByPrimaryKey默认是更新所有字段包括null。如果你用前端传来的对象直接做更新表单里没填的字段会被置空。所以很多团队会改成手写updateByPrimaryKeySelective这只更新非空字段。这个差异初看不明显上线后就是数据丢失事故级别的坑。5. 生成代码如何融入Spring Boot项目5.1 Mapper扫描配置Spring Boot项目里需要让容器知道Mapper接口在哪。两种方式方式一启动类上加MapperScanSpringBootApplication MapperScan(com.example.demo.mapper) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }方式二每个Mapper接口上加MapperMapper public interface OrderMapper { // ... }推荐方式一一个注解搞定所有Mapper不用每个接口重复标注。5.2 XML映射文件的位置约定MBG生成的XML在src/main/resources/mapperSpring Boot默认不扫描这个目录。需要配置MyBatis的mapper-locations属性。如果用的是mybatis-spring-boot-starter在application.yml里mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.entity这里有个坑如果忘了配置mapper-locations运行时调用Mapper方法会报Invalid bound statement (not found)。这个报错信息非常常见半数新手都踩过。排查的第一件事就是确认XML扫描路径是否配置正确。5.3 Service层怎么用生成好的Mapper写一个演示用的ServiceService public class OrderService { Autowired private OrderMapper orderMapper; public Order getById(Long id) { return orderMapper.selectByPrimaryKey(id); } public ListOrder getAllOrders() { return orderMapper.selectAll(); } public void createOrder(Order order) { orderMapper.insert(order); } }5.4 实体类的扩展注意点生成的实体类跟数据库表字段一一对应但业务上经常需要额外字段。比如订单列表页要展示用户名你可能会在Order实体里加一个userName字段。我强烈建议不要直接改生成的实体类文件而是新建一个VO类继承或聚合它。原因很简单生成器是随时可能重跑的。如果表结构加了字段你重跑一次MBGOrder.java会被覆盖overwritetrue时你手加的字段和对应的getter/setter全没了。即使overwritefalse生成器检测到文件存在会跳过你依然得不到新字段。最稳妥的方案是实体类Order保持和表结构严格对应扩展字段放进OrderVO、OrderQuery这类专门的类复杂查询直接用Select注解写在Mapper接口里或者手写XML这样重跑生成器的代价几乎为零。6. 高频报错与排查链路我遇到的坑都在这了6.1 坑一表名带前缀生成的实体名不符合预期这个问题本质上是MBG的表名到类名的转换规则完全依赖domainObjectName的显示配置。没有配置时t_order会被转换成一个很奇怪的TOrder。为什么会这样因为MBG的表名转换算法只是简单地把下划线去掉、首字母大写t_order去掉下划线就是Torder再校正首字母大写得到TOrder。我见过有人的解决方案是在表名上加反引号或者写tableNamet_order然后忍受TOrder的类名。这显然不够优雅。正确的做法就一句每个table都显式写domainObjectName。一张表一行配置清晰可控。6.2 坑二再次生成时业务代码被覆盖MBG的overwrite参数只在Maven插件的configuration里配置但很多人不知道它仅对XML映射文件生效Java文件不会因为overwritetrue而强制覆盖——MBG对Java文件的保护策略是如果同名文件已存在默认跳过而不是覆盖。这个设计让很多人产生误解。实际行为是这样的XML映射文件overwritetrue时直接覆盖Java文件即使overwritetrue如果同路径文件已存在生成器会跳过并写日志所以重跑生成器你的Java实体类如果手工改过不会丢。但这也是隐患——表结构变了之后重跑实体类如果没有被更新你手动加的新字段、新接口里用的新属性就会编译报错。这时候要手动删掉旧文件再跑一次。我个人的工作流是表结构变化后先手工对比哪些文件是纯生成物把对应的实体类、Mapper接口、XML文件删掉再重跑生成。虽然多了一步但是心里有底不会出现新旧代码残留的脏状态。6.3 坑三JDBC驱动版本和数据库版本不匹配这个问题在升级MySQL 8后尤其典型。你的数据库是MySQL 8.0.x但pom里驱动还是5.1.47跑生成器会报连接失败或驱动类找不到。驱动类名变了MySQL 5.x驱动类是com.mysql.jdbc.DriverMySQL 8.x是com.mysql.cj.jdbc.Driver。配置里如果写的是旧的类名MBG会直接ClassNotFound。另外一个相关问题是驱动只能在插件里用如果插件的dependencies漏了驱动依赖报错也是ClassNotFoundException: com.mysql.cj.jdbc.Driver。这两个地方都排查一下。6.4 坑四表注释里的特殊字符导致XML解析失败MySQL表注释里如果写了emoji或者其他特殊字符MBG读取后写入XML注释时可能产生编码问题。有些团队的表注释写着写着就带上了火星文生成完的XML文件用IDE打开是乱码然后编译报错。解决办法生成前确保数据库连接的URL里配置了characterEncodingutf8同时generatorConfig.xml文件本身保存为UTF-8编码。还有一点如果是Windows环境注意有些编辑器默认保存为GBK也要统一转成UTF-8。6.5 坑五想用的字段被生成了不想用的字段也生成了表结构是DBA统一设计的但不同业务方用到的字段不一样。比如user表有个internal_remark字段是内部运营备注普通业务方根本不该碰。MBG的table节点可以配置列的包含/排除table tableNameuser domainObjectNameUser ignoreColumn columninternal_remark/ /table加了ignoreColumn后生成的实体类、Mapper、XML里完全不会出现这个字段。适合做API层接口隔离的场景直接从源头杜绝误用。7. 进阶优化让生成结果更贴合团队需求7.1 targetRuntime怎么选MyBatis3 vs MyBatis3Simple前面提了一嘴这里展开讲透。这两个运行时生成的代码风格差异很大特性MyBatis3MyBatis3Simple生成方法数很多包含Example系列精简基础CRUD是否有Example类是否单表动态查询通过Example实现不支持代码量膨胀精简适用场景复杂查询需求多的项目以单表普通CRUD为主我的判断标准很简单如果你刚开始做项目先上MyBatis3Simple。等确实出现了需要动态条件查询的表再单独把那张表生成两次——可以针对同一张表加两个table节点一个用Simple运行时另一个用MyBatis3运行时分别生成不同命名的实体和Mapper灵活得很。但大多数情况下用Simple 手写扩展XML就够了。Example这个东西写起来绕读起来也绕团队协作成本高。7.2 自定义注释生成器让生成的代码带上自己的规范如果你不想完全关掉注释又不想每次生成都产出一堆时间戳diff可以写一个自定义的CommentGenerator。思路是继承DefaultCommentGenerator重写addComment和addJavaFileComment方法package com.example.demo.generator; import org.mybatis.generator.api.CommentGenerator; import org.mybatis.generator.api.IntrospectedColumn; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.internal.DefaultCommentGenerator; import org.mybatis.generator.internal.util.StringUtility; import java.util.Properties; public class CustomCommentGenerator extends DefaultCommentGenerator { Override public void addConfigurationProperties(Properties properties) { super.addConfigurationProperties(properties); } Override public void addFieldComment(org.mybatis.generator.api.dom.java.Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { // 直接使用数据库字段注释 if (introspectedColumn.getRemarks() ! null !introspectedColumn.getRemarks().isEmpty()) { field.addJavaDocLine(/**); field.addJavaDocLine( * introspectedColumn.getRemarks()); field.addJavaDocLine( */); } } }然后在generatorConfig.xml里指定commentGenerator typecom.example.demo.generator.CustomCommentGenerator这样一个字段注释会直接取数据库表的COMMENT生成的实体类可读性大幅提升而且不含时间戳不会产生无意义的Git diff。7.3 接入Lombok让实体类瘦身默认生成的实体类每个字段都带一对getter/setter一个20个字段的表光getter/setter就一百多行。如果你的团队在用Lombok可以让生成的实体类只保留字段声明类上打Data注解。实现方式有两种方式一用MBG自带的lombok扩展点。在context里配置plugin typeorg.mybatis.generator.plugins.LombokPlugin这个插件需要你自己实现核心是重写modelBaseRecordClassGenerated方法在生成的类上添加Data注解并抑制getter/setter的生成。方式二用第三方的mybatis-generator-lombok-plugin网上有现成的开源项目。引入后只需要plugin typecom.softwareloop.mybatis.generator.plugins.LombokPlugin我的建议是如果你愿意折腾自己写一个也就二三十行代码不想折腾直接引入现成的插件。7.4 集成到Maven生命周期一键生成省心省力默认情况下生成器需要手动执行mvn mybatis-generator:generate如果项目有CI/CD可以把生成器绑定到generate-sources阶段plugin groupIdorg.mybatis.generator/groupId artifactIdmybatis-generator-maven-plugin/artifactId version1.4.0/version executions execution idgenerate-mybatis-code/id phasegenerate-sources/phase goals goalgenerate/goal /goals /execution /executions /plugin这样每次mvn compile或mvn package时自动执行生成配合overwritetrue代码始终与表结构同步。但我不建议无脑这么做原因还是前面说的自动覆盖会让手工改动在编译时悄然消失。更稳妥的做法是表结构变更后手动触发生成生成结果提交到Gitcode review后合并。这和我前面说的“删掉旧文件再重跑”的思路一致。7.5 与MyBatis-Plus、tk.mybatis的取舍到了2021年市面上已经有MyBatis-Plus这类增强框架自带BaseMapper单表CRUD连生成XML都省了。那还有必要用MBG吗我自己的看法是看团队情况。MyBatis-Plus优势在于封装更彻底单表CRUD完全零代码甚至分页插件、逻辑删除都内置了。但它的问题也很明显一旦你发现自己需要精细控制SQL它的实体注解体系会带来额外的理解成本而且它的XML能力依然要自己写Mapper.xml只是多了一个BaseMapper的自动CRUD兜底。MBG的优势在于生成的是标准MyBatis代码透明可修改没有任何框架魔法。对于要求SQL完全可控、团队风格统一、需要代码评审看了心里踏实的团队MBG更合适。还有一个更实际的角度如果团队已经用MyBatis-Plus写了不少业务代码这时候引入MBG反而会让代码风格割裂一半是BaseMapper自动CRUD一半是生成的手写CRUD维护成本反而升高。技术选型这事没有绝对的对错只有适不适合当下的团队和项目阶段。8. 命令行和Java API方式补充两种运行姿势8.1 命令行方式解耦Maven快速试用有时候你只是想快速生成几个文件看看效果不想动pom.xml命令行方式最合适。需要先下载jar包wget https://repo1.maven.org/maven2/org/mybatis/generator/mybatis-generator-core/1.4.0/mybatis-generator-core-1.4.0.jar还需要MySQL驱动jar包然后执行java -cp mybatis-generator-core-1.4.0.jar:mysql-connector-java-8.0.25.jar \ org.mybatis.generator.api.ShellRunner \ -configfile generatorConfig.xml -overwriteWindows用户把冒号换成;。需要同目录下有generatorConfig.xml文件。命令行方式适合本机快速试验但对团队协作不友好配置文件、jar包版本容易各自为战。8.2 Java API方式可以封装成自己的生成工具如果你的团队有统一开发平台想提供一个网页端或IDE插件的生成入口可以用Java API方式。核心代码package com.example.demo.generator; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; import java.io.File; import java.util.ArrayList; import java.util.List; public class GeneratorRunner { public static void main(String[] args) throws Exception { ListString warnings new ArrayList(); boolean overwrite true; File configFile new File(src/main/resources/generator/generatorConfig.xml); ConfigurationParser cp new ConfigurationParser(warnings); Configuration config cp.parseConfiguration(configFile); DefaultShellCallback callback new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); for (String warning : warnings) { System.out.println(warning); } } }这个入口可以做成一个main方法直接跑也可以做成Spring Boot的CommandLineRunner。实际项目中我曾经把它封装成一个内部工具开发者在网页上勾选表名、填好包名后端调用这串代码生成ZIP包下载。上手难度不大但省了大家挨个配Maven插件的功夫。9. 从我实际使用MBG两年半的经验谈几点感受回到开头那个问题为什么还要自己写Mapper答案其实很朴素——重复的活需要自动化但自动化的边界要清晰。MBG这两年半用下来我的感觉是它最适合的定位是项目初期的“脚手架加速器”和“代码风格统一器”。一个新项目几十张表手写一遍CRUD要两三天用MBG五分钟搞定而且每张表的代码结构一模一样新同事接手几乎没有学习成本。但也要认清它的短板。遇到多表关联、复杂子查询、需要深度优化的分页SQLMBG生成的代码帮不上忙这些还是得自己动手。甚至它的Example机制在复杂场景下会变成负担——为了查一个简单条件你得创建Example、创建Criteria代码绕得不行。我自己的实践习惯是这样的表结构交付后立即用MBG生成基础代码作为项目的地基实体类严格保持与表结构一致扩展字段一律放VOXML映射文件里的手工SQL用注释块区分开标上“手工维护”四个字避免误覆盖每次重跑生成器之前先看一眼Git工作区有没有未提交的手工改动再决定要不要清理旧文件生成结果必须走代码评审因为表结构注释乱、字段命名不规范这些问题会在生成代码里原样暴露最后分享一个小技巧如果数据库表字段注释写得规范生成的代码可读性会大幅提升。所以与其吐槽MBG生成的东西丑不如先花点时间把表注释维护好。一个注释清晰的user表生成的User.java读起来跟手写的一样顺眼。工具只是放大了你的输入质量这个道理在代码生成器上体现得尤其明显。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进