☰
MyBatis逆向工程:从数据库Schema到可维护Mapper的工程化实践
2026/10/14 19:58:02 网站建设 项目流程

1. 项目概述:为什么“MyBatis逆向工程”不是偷懒捷径,而是团队提效的底层基建

你有没有经历过这样的场景:一个新数据库表上线,字段12个,关联3张字典表,业务接口要查、要改、要分页、要导出——光是手写Mapper XML里的resultMap、sql片段、parameterType声明,就得花掉大半天;更别说还要同步维护对应的Java实体类、Mapper接口方法签名、Service层调用逻辑。我带过的几个后端小组里,新人平均在“写基础DAO层”上卡壳2~3天,老手也常因字段名拼错、类型映射漏配、动态SQL条件顺序混乱,在联调阶段反复返工。而“MyBatis逆向工程自动生成mapper映射”这件事,本质上不是让开发者“不写代码”,而是把重复、机械、高错误率、低创造性的那部分工作,从人脑中剥离出来,交给一套可验证、可追溯、可批量执行的标准化流程来完成。

它解决的从来不是“会不会写SQL”的问题,而是“要不要把时间浪费在写100遍几乎一样的 标签”上的问题。核心关键词——MyBatis、逆向工程、自动生成、Mapper映射——每一个都指向明确的技术动作:MyBatis是持久层框架选型;逆向工程(Reverse Engineering)特指从已有数据库结构反向推导出代码结构的过程;自动生成强调零人工干预的脚本化输出;Mapper映射则是最终交付物的核心形态,包含Java实体类(POJO)、Mapper接口(含注解或XML定义)、XML映射文件(含SQL与结果集映射规则)三件套。它适合三类人:刚接手遗留系统的维护者(快速理清表-对象关系)、中台型项目组(统一生成标准DTO/VO/Entity,避免各模块自行定义导致不一致)、以及需要高频迭代数据模型的SaaS产品团队(比如每两周新增一张用户行为日志表,必须当天完成接入)。这不是给懒人准备的“一键生成器”,而是给认真做事的人配备的“防错校验仪”和“产能加速器”。

我试过最极端的案例:某高校实验室的科研数据管理平台,初期由3位研究生协作开发,数据库含87张表,其中62张为元数据配置表(如仪器参数模板、实验流程节点、权限角色映射)。手动编写Mapper耗时11人日,且上线后发现5处字段类型映射错误(tinyint被当成Integer而非Boolean)、3处resultMap未处理空值导致NPE。引入逆向工程后,同一套配置跑一次,12分钟生成全部基础代码,人工仅需检查3处业务特殊逻辑(如密码字段需加密存储,不参与通用查询)。后续新增表时,运维同学只需改一行配置,执行一个脚本,新表的CRUD能力就已就绪。这背后不是魔法,而是把“数据库Schema”这个唯一可信源,通过确定性规则,映射到“Java代码结构”这个可执行产物上——中间所有环节都可审计、可回滚、可版本化。所以别再把它当成“黑盒工具”,它其实是你团队代码规范的第一道防线。

2. 核心设计思路拆解:为什么必须放弃“IDE插件一键生成”,转而构建可复现的工程化流程

很多人第一次接触逆向工程,第一反应是打开IDEA,装个Free MyBatis plugin,右键数据库表点“Generate POJOs And Mappers”,几秒钟弹出一堆文件——看起来很美。但我在某次跨团队协作中亲眼见过这套方案翻车:A组用插件生成的Mapper接口里,方法名是selectByUserId,B组用另一款插件生成的却是findByUserId,C组手写的又是queryUserById。三个模块对接时,Service层调用方根本不知道该用哪个方法,最后靠文档对齐+人工改名,耗时两天。更致命的是,插件生成的代码不进Git,不走CI,没人知道它基于哪个数据库快照、用了什么命名策略、是否排除了审计字段。当线上出现NullPointerException时,排查路径直接断裂:是数据库字段改了?是插件版本升级了?还是某人本地误操作覆盖了生成文件?

因此,真正可靠的逆向工程,必须脱离IDE环境,走向工程化、脚本化、版本化。它的核心设计逻辑有三层硬约束:

第一层是输入源唯一性:所有生成动作必须基于一份明确的、版本受控的数据库连接配置(JDBC URL、用户名、密码),且该配置应存于项目根目录下的generatorConfig.xml或pom.xml的plugin配置块中,禁止硬编码在IDE设置里。我坚持要求团队把数据库连接信息抽离成generator.properties文件,并通过Maven Profile控制不同环境(dev/test/prod)的生成目标库,这样开发时连本地MySQL,测试时连Docker里的测试库,完全隔离无干扰。

第二层是规则可声明化:什么表该生成?什么字段该忽略?主键怎么识别?驼峰命名如何转换?这些不能靠插件UI里勾选,而必须写成XML或YAML中的显式规则。比如<table tableName="sys_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false"/>,明确告诉生成器:只处理这张表,生成的Java类叫User,且禁用Example模式(避免生成大量冗余的Criteria类)。我们曾因没禁用Example,在一个200+字段的大宽表上生成了17MB的Mapper XML,编译直接失败。

第三层是输出可追溯化:每次生成必须记录元信息——生成时间、数据库版本号(SELECT VERSION())、MyBatis Generator版本、所用配置文件的Git commit hash。我在build/generated-sources/mybatis/目录下强制添加GENERATION_LOG.md,内容类似:

Generated at: 2024-06-15T14:22:03+08:00 DB Version: MySQL 8.0.33 Generator Version: 1.4.2 Config Commit: a3f9c2d (feat: add user_behavior_log table support)

这样当某天发现生成的createTime字段在Java里是Date类型,而数据库是datetime(3),就能立刻定位是哪次配置变更引入的问题,而不是在历史提交里大海捞针。

放弃插件,不是拒绝便利,而是把“便利”的控制权从IDE厂商手里,夺回到开发者自己手中。真正的效率,来自确定性,而非速度。

3. 核心细节解析与实操要点:从数据库建模到生成配置的12个关键决策点

逆向工程看似“配置好就跑”,实则每个选项背后都是对项目长期可维护性的投票。我整理了从数据库建模开始,到最终生成代码为止,必须亲手拍板的12个关键决策点,每个都附带血泪教训。

3.1 数据库命名规范:下划线转驼峰不是万能解药

很多团队默认开启<property name="useActualColumnNames" value="false"/>,让生成器自动把user_name转成userName。但问题来了:user_id转成userId没问题,user_id_card呢?是userIdCard还是UserIdCard?我们曾有个表字段叫is_deleted_flag,按规则转成isDeletedFlag,但业务语义上它就是个布尔标志,应该叫deleted。解决方案是:在数据库建模阶段就约定前缀规则。我们强制要求:布尔字段必须以is_或has_开头(如is_active,has_permission),生成时通过<columnOverride column="is_deleted_flag" property="deleted" javaType="java.lang.Boolean"/>显式重命名。这样既保持数据库语义清晰,又让Java侧API简洁。

3.2 主键策略:自增ID与UUID必须区别对待

<table>标签的enableInsertSelective和enableUpdateByPrimaryKey属性,表面看是开关,实则影响整个DAO层设计哲学。对于MySQL自增主键表(如order_info),我们开启enableUpdateByPrimaryKey=true,因为业务上必然存在“根据订单ID更新状态”的场景;但对于使用UUID作为主键的file_metadata表,我们关闭它,因为UUID主键极少被业务代码直接修改,强行生成updateByPrimaryKey方法反而误导调用方。更关键的是<generatedKey>配置:<generatedKey column="id" sqlStatement="MySql" identity="true"/>必须精确匹配数据库实际策略,否则生成的insertSelective会漏掉主键赋值逻辑,导致插入后ID为null。

3.3 字段类型映射:别让JDBC驱动替你做决定

MyBatis Generator默认用JDBC的getJdbcType()推断类型,但MySQL的tinyint(1)会被当成TINYINT,对应Java的Byte,而业务上它99%是布尔值。我们必须用<columnOverride>强制指定:

<columnOverride column="status" javaType="java.lang.Boolean" jdbcType="BIT"/>

同理,decimal(10,2)默认映射为java.math.BigDecimal,但财务系统要求精度绝对可控,我们额外增加<javaProperty>配置,确保生成的getter/setter方法名符合getAmount()而非getDecimalAmount()。

3.4 Example模式:何时该砍掉这颗“毒瘤”

enableCountByExample和enableSelectByExample默认为true,会生成XxxExample类及一堆countByExample、selectByExample方法。但真实业务中,90%的查询都有明确业务语义(如findActiveUsersByDeptId),而非泛化的where status = ? and dept_id = ?。我们规定:只有配置表、字典表允许启用Example(因其查询模式高度通用),业务主表一律关闭。关闭后,生成的Mapper接口干净利落,且迫使开发人员在Service层显式编写有业务含义的查询方法,避免后期满屏xxxExample.createCriteria().andStatusEqualTo(1)的面条代码。

3.5 XML与注解二选一:XML才是企业级项目的事实标准

虽然MyBatis支持@Select("SELECT * FROM user")注解方式,但逆向工程中我们坚持用XML。原因有三:一是XML支持动态SQL(<if>,<choose>)的复杂组合,注解里写@Select("<script>SELECT * FROM user <if test='name!=null'>WHERE name=#{name}</if></script>")可读性极差;二是XML文件可被IDE的SQL插件语法高亮、执行测试,注解里的SQL是纯字符串;三是XML便于统一管理SQL审核规则(如禁止SELECT *,必须指定字段列表),我们通过SonarQube规则扫描*.xml文件中的<select>标签,自动拦截违规SQL。生成配置中<javaClientGenerator>的type="XMLMAPPER"是唯一选择。

3.6 包路径与文件名:拒绝“默认包”陷阱

<javaModelGenerator>的targetPackage必须精确到com.xxx.project.entity,而非entity。曾有团队图省事设为model,结果生成的User.java在src/main/java/model/User.java,而Spring Boot启动类在com.xxx.Application,导致组件扫描失效。更隐蔽的坑是targetProject路径:targetProject="src/main/java"是常规写法,但如果项目是多模块Maven,必须写成targetProject="mybatis-generator/src/main/java",否则生成文件会跑到错误模块下。我们用mvn mybatis-generator:generate -Dmybatis.generator.overwrite=true命令时,永远加-D参数覆盖旧文件,避免手动清理残留。

3.7 枚举字段处理:数据库里存数字,Java里要枚举类

user_status字段存0/1/2,业务上对应DISABLED/ACTIVE/LOCKED。逆向工程不会自动生成枚举,必须手动介入。我们的做法是:在generatorConfig.xml中配置<columnOverride column="user_status" javaType="com.xxx.enums.UserStatus"/>,然后在src/main/java/com/xxx/enums/下提前创建好UserStatus.java,内容为标准枚举:

public enum UserStatus { DISABLED(0), ACTIVE(1), LOCKED(2); private final int value; UserStatus(int value) { this.value = value; } public int getValue() { return value; } }

生成器会自动在User实体类中将status字段类型设为UserStatus,并在UserMapper.xml的<resultMap>中添加<result column="user_status" property="status" javaType="com.xxx.enums.UserStatus"/>,彻底解决类型安全问题。

3.8 逻辑删除字段:不止是加个@TableLogic

MyBatis-Plus的@TableLogic注解很香,但逆向工程不认它。我们必须在生成后手动修改:一是在User实体类的deleted字段上加@TableLogic;二是在UserMapper.xml的<sql>片段中,为所有SELECT语句追加AND deleted = 0条件;三是在<delete>语句中,将DELETE FROM user WHERE id = #{id}改为UPDATE user SET deleted = 1 WHERE id = #{id}。更稳妥的做法是:在generatorConfig.xml中,用<sql>标签定义全局base_where_clause,并在每个<select>的<where>内引用它,确保逻辑删除条件不遗漏。

3.9 分页插件兼容:PageHelper的<page>标签必须手写

PageHelper的startPage方法只对紧随其后的第一个SELECT生效。逆向工程生成的selectByExample方法若返回List<Xxx>,PageHelper能自动分页;但若返回List<XxxWithBLOBs>(含大字段),PageHelper可能失效。我们的对策是:在generatorConfig.xml中,为含BLOB字段的表单独配置<table tableName="article" enableSelectByExample="false"/>,然后手写selectArticleList方法,在XML中明确使用<page>标签包裹SQL,并在Java接口中声明返回Page<Article>。这样分页逻辑完全可控,不依赖PageHelper的“魔法”。

3.10 生成文件过滤:.gitignore不是万能的

生成的代码默认放在target/generated-sources/mybatis/,但有些团队会把target/加入.gitignore,导致CI流水线拉取代码后无法编译——因为target/是Maven构建产物,不应进Git。正确做法是:在pom.xml中配置<outputDirectory>${project.basedir}/src/main/java</outputDirectory>,让生成文件直落src/main/java,并把生成目录加入.gitignore(如src/main/java/com/xxx/entity/generated/),同时在README.md中注明“运行mvn mybatis-generator:generate生成基础代码”。这样开发者本地可随时重生成,CI环境通过Maven生命周期自动触发。

3.11 多数据源场景:一个配置文件不够用

微服务架构下,订单库、用户库、商品库物理分离。逆向工程不能共用一个generatorConfig.xml。我们的方案是:在项目根目录建generator/子目录,下设order-db.xml、user-db.xml、product-db.xml,每个文件独立配置对应数据源。执行时用mvn mybatis-generator:generate -Dmybatis.generator.configurationFile=generator/order-db.xml指定文件。更进一步,我们用Maven Profiles绑定数据源:mvn mybatis-generator:generate -P order-db,Profile中激活对应配置文件,彻底解耦。

3.12 生成后代码增强:Lombok不是银弹

很多人开<property name="useLombok" value="true"/>,生成的实体类加@Data。但@Data包含@ToString,当实体含循环引用(如User有List<Role>,Role又有List<User>)时,toString()会栈溢出。我们的实践是:关闭Lombok生成,改用<property name="useLombok" value="false"/>,然后在User.java顶部手动加@Getter @Setter @NoArgsConstructor @AllArgsConstructor,精准控制所需特性。@ToString和@EqualsAndHashCode必须显式排除关联集合字段,如@ToString(exclude = "roles")。

提示:所有<columnOverride>配置必须在<table>标签内部,不能写在全局<context>里,否则不生效。这是MyBatis Generator的硬性规则,踩过坑才知道。

4. 实操过程与核心环节实现:从零搭建可落地的逆向工程流水线

现在我们进入实操环节。以下步骤基于Maven + MyBatis Generator 1.4.2 + MySQL 8.0,所有命令和配置均经生产环境验证。不要跳步,每个环节的参数选择都有明确意图。

4.1 环境准备:三步锁定最小可行依赖

第一步,确认JDK版本。MyBatis Generator 1.4.x要求JDK 8+,但若项目用JDK 17,需注意javax.annotation包已被移除,必须在pom.xml中显式添加:

<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>

第二步,添加MyBatis Generator Maven插件。注意groupId是org.mybatis.generator,不是org.mybatis:

<plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.2</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> </dependencies> </plugin>

第三步,创建src/main/resources/generatorConfig.xml。这是整个流程的“宪法”,必须严格遵循DTD规范。我们采用最简但最健壮的结构:

<?xml version="1.0" encoding="UTF-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 resource="generator.properties"/> <context id="MysqlContext" targetRuntime="MyBatis3" defaultModelType="flat"> <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/> <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/> <commentGenerator> <property name="suppressAllComments" value="true"/> <property name="addRemarkComments" value="true"/> </commentGenerator> <jdbcConnection driverClass="${driver}" connectionURL="${url}" userId="${username}" password="${password}"/> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <javaModelGenerator targetPackage="com.xxx.entity" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <javaClientGenerator type="XMLMAPPER" targetPackage="com.xxx.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 表配置在此处 --> <table tableName="sys_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false"> <columnOverride column="create_time" property="createTime" javaType="java.time.LocalDateTime"/> <columnOverride column="update_time" property="updateTime" javaType="java.time.LocalDateTime"/> </table> </context> </generatorConfiguration>

关键点解析:<plugin>引入SerializablePlugin确保实体类实现Serializable,ToStringPlugin生成toString()方法;<commentGenerator>关闭自动生成注释(避免中文乱码),但开启addRemarkComments保留数据库字段注释;<javaTypeResolver>设forceBigDecimals=false防止decimal全转BigDecimal;<table>内嵌<columnOverride>精准控制时间字段类型。

4.2 配置文件分离:generator.properties的黄金法则

src/main/resources/generator.properties内容如下:

# 数据库连接 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/my_project?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false username=root password=123456 # 生成路径(绝对路径或相对路径) targetJavaProject=src/main/java targetResourcesProject=src/main/resources # 命名策略 useActualColumnNames=false autoDelimitKeywords=true # 其他 contextId=MysqlContext

这里serverTimezone=Asia/Shanghai是MySQL 8.0必需参数,否则连接报错;allowPublicKeyRetrieval=true解决SSL握手问题;autoDelimitKeywords=true让生成器自动给关键字字段(如order、group)加反引号,避免SQL语法错误。所有敏感信息(密码)绝不硬编码在XML中,这是安全底线。

4.3 执行生成:一条命令背后的完整生命周期

在项目根目录执行:

mvn mybatis-generator:generate -Dmybatis.generator.overwrite=true -X

-X参数开启Debug日志,能看到每一步执行细节。成功时输出类似:

[INFO] Scanning for projects... [INFO] [INFO] ------------------< com.xxx:my-project >------------------ [INFO] Building my-project 1.0.0 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- mybatis-generator-maven-plugin:1.4.2:generate (default-cli) @ my-project --- [DEBUG] Configuring mojo 'org.mybatis.generator:mybatis-generator-maven-plugin:1.4.2:generate' with basic configurator --> [INFO] Connecting to the Database... [INFO] Introspecting table sys_user [INFO] Generating Record class for table sys_user [INFO] Generating Mapper Interface for table sys_user [INFO] Generating SQL Map for table sys_user [INFO] Saving file User.java [INFO] Saving file UserMapper.java [INFO] Saving file UserMapper.xml [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------

注意Introspecting table表示正在读取数据库元数据,若卡住,说明JDBC连接不通;Saving file表示文件写入成功。生成的文件位置:

  • src/main/java/com/xxx/entity/User.java
  • src/main/java/com/xxx/mapper/UserMapper.java
  • src/main/resources/mapper/UserMapper.xml

4.4 生成文件精修:三处必改的“最后一公里”

生成的代码不是终点,而是起点。必须手工修改三处:

第一处:Mapper接口的@Mapper注解
UserMapper.java默认没有注解,Spring Boot无法扫描。必须在接口上加:

@Mapper public interface UserMapper { // 方法定义 }

或者在启动类上加@MapperScan("com.xxx.mapper"),二者选一。我们倾向前者,因为更细粒度可控。

第二处:XML文件的namespace与resultMapID
UserMapper.xml中<mapper namespace="com.xxx.mapper.UserMapper">必须与接口全限定名一致;<resultMap id="BaseResultMap" ...>的ID必须唯一,若多张表生成,需确保ID不冲突。我们约定:BaseResultMap用于基础字段,ResultMapWithBLOBs用于含大字段的映射,ID前缀加表名缩写,如UserBaseResultMap。

第三处:实体类的@TableName与@TableField
若项目集成MyBatis-Plus,需在User.java上加@TableName("sys_user"),在字段上加@TableField("user_name"),否则MP的自动填充、逻辑删除等功能失效。@TableField的exist = false用于忽略数据库不存在的字段(如DTO扩展字段)。

4.5 CI/CD集成:让生成成为构建的自然环节

在Jenkins或GitLab CI中,将逆向工程纳入构建流程。.gitlab-ci.yml示例:

stages: - generate - build generate-mapper: stage: generate image: maven:3.8-openjdk-11 script: - mvn mybatis-generator:generate -Dmybatis.generator.overwrite=true artifacts: - src/main/java/com/xxx/entity/** - src/main/java/com/xxx/mapper/** - src/main/resources/mapper/** build-app: stage: build image: maven:3.8-openjdk-11 script: - mvn clean package -DskipTests dependencies: - generate-mapper

关键点:generate-mapper阶段生成代码并作为artifacts传递给build-app,确保编译时代码已就绪。这样每次Push数据库变更脚本(如V2024.06.15__add_user_status.sql),CI自动触发生成,新代码随构建产物发布,全程无人值守。

注意:mvn mybatis-generator:generate命令必须在compile阶段之前执行,否则生成的Java文件不会被编译。Maven生命周期中,generate-sources是正确时机,因此插件配置中<phase>generate-sources</phase>是最佳实践。

5. 常见问题与排查技巧实录:那些官方文档不会告诉你的17个坑

逆向工程最大的挑战不是配置,而是问题排查。以下是我在多个项目中积累的真实问题速查表,按发生频率排序,每个都附带现场诊断命令和修复方案。

5.1 问题:生成后编译报错“找不到符号”,提示UserMapper类不存在

现场诊断:

# 检查生成文件是否真的存在 ls -l src/main/java/com/xxx/mapper/UserMapper.java # 检查Maven是否识别为源码目录 mvn help:effective-pom | grep -A 5 "<sourceDirectory>"

根因:targetProject路径错误,文件生成到了target/目录下,而Maven只编译src/main/java。
修复:在generatorConfig.xml中,将<javaClientGenerator>的targetProject明确设为src/main/java,而非默认的target/generated-sources/mybatis。

5.2 问题:生成的User.java中,createTime字段类型是java.util.Date,但数据库是datetime(3)

现场诊断:

-- 查看字段精确类型 DESCRIBE sys_user; -- 输出:create_time | datetime(3) | YES | NULL | NULL |

根因:MyBatis Generator的javaTypeResolver未适配MySQL 8.0的毫秒精度。
修复:在<javaTypeResolver>中添加<property name="useJSR310Types" value="true"/>,并确保JDK 8+,这样datetime映射为java.time.LocalDateTime。

5.3 问题:mvn mybatis-generator:generate执行后无任何输出,也不报错

现场诊断:

# 开启Debug日志 mvn mybatis-generator:generate -X | grep -i "generator\|error" # 检查配置文件路径 ls -l src/main/resources/generatorConfig.xml

根因:configurationFile路径错误,或generator.properties文件名拼错(如generator.property少了个s)。
修复:在pom.xml插件配置中,用绝对路径测试:<configurationFile>${project.basedir}/src/main/resources/generatorConfig.xml</configurationFile>。

5.4 问题:生成的UserMapper.xml中,<select>语句的resultMap引用BaseResultMap,但实际定义的是UserBaseResultMap

现场诊断:

# 检查XML中resultMap定义 grep -n "<resultMap id=" src/main/resources/mapper/UserMapper.xml # 检查select中引用 grep -n "resultMap=" src/main/resources/mapper/UserMapper.xml

根因:<table>标签未设置domainObjectName="User",导致生成器用默认名SysUser,resultMapID变为SysUserBaseResultMap。
修复:在<table>中强制指定domainObjectName,并确保首字母大写。

5.5 问题:UserMapper.selectByExample方法返回空List,但数据库明明有数据

现场诊断:

-- 手动执行生成的SQL(从XML中复制) SELECT * FROM sys_user WHERE status = ? AND deleted = 0; -- 参数填1,看是否有结果

根因:Example类的createCriteria()默认条件是AND,但业务需要OR;或deleted字段未在Example中设置,导致WHERE deleted = null。
修复:关闭enableSelectByExample,手写带业务语义的方法;或在调用时显式设置example.createCriteria().andDeletedEqualTo(false)。

5.6 问题:生成的User.java中,userName字段的getter方法是getUsername(),而非getUserName()

现场诊断:

# 检查生成器的驼峰规则 cat src/main/resources/generatorConfig.xml | grep -A 5 "useActualColumnNames"

根因:useActualColumnNames="true"时,生成器用数据库列名user_name直接转,user_name→username;设为false才启用驼峰转换。
修复:<property name="useActualColumnNames" value="false"/>,并确认<columnOverride>未覆盖该字段。

5.7 问题:mvn compile报错“程序包com.xxx.mapper不存在”

现场诊断:

# 检查生成的Mapper接口是否在正确包路径 ls -l src/main/java/com/xxx/mapper/ # 检查pom.xml中是否遗漏<plugin>配置 mvn help:effective-pom | grep -A 10 "mybatis-generator"

根因:<javaClientGenerator>的targetPackage设为mapper,但targetProject是src/main/java,导致文件生成在src/main/java/mapper/,而非src/main/java/com/xxx/mapper/。
修复:targetPackage必须是完整包名,如com.xxx.mapper。

5.8 问题:生成的UserMapper.xml中,<insert>语句未包含<selectKey>,自增ID返回为null

现场诊断:

-- 查看表结构 SHOW CREATE TABLE sys_user; -- 确认id字段是AUTO_INCREMENT

根因:<table>未配置<generatedKey>,或identity="true"写成identity="false"。
修复:添加<generatedKey column="id" sqlStatement="MySql" identity="true"/>到<table>内部。

5.9 问题:UserMapper.updateByPrimaryKey方法执行后,数据库记录未更新

现场诊断:

-- 查看生成的SQL SELECT * FROM sys_user WHERE id = 1; -- 手动执行update语句(从XML复制) UPDATE sys_user SET user_name = ? WHERE id = ?;

根因:<table>的enableUpdateByPrimaryKey="false",但接口中仍调用该方法;或<update>语句中WHERE条件漏了主键。
修复:确认enableUpdateByPrimaryKey="true",并检查生成的XML中<update>标签的WHERE子句是否包含id = #{id,jdbcType=BIGINT}。

5.10 问题:生成的User.java中,@Data注解导致toString()栈溢出

现场诊断:

# 运行单元测试,触发toString() User user = new User(); user.toString(); // 报StackOverflowError

根因:实体间存在双向关联,@Data生成的toString()递归调用。
修复:关闭useLombok,手动添加@Getter @Setter,并用@ToString(exclude = {"roles", "orders"})排除集合字段。

5.11 问题:mvn mybatis-generator:generate报错“Cannot resolve symbol 'MyBatisGenerator'"

现场诊断:

# 检查插件依赖 mvn dependency:tree | grep -i "mybatis-generator"

根因:插件<dependencies>中未声明mysql-connector-java,或版本与JDBC URL不匹配。
修复:在<plugin>的<dependencies>中,添加与url中MySQL版本匹配的驱动,如MySQL 8.0用8.0.33。

5.12 问题:生成的UserMapper.xml中,<resultMap>的<id>标签未生成,导致一级缓存失效

现场诊断:

# 检查resultMap定义 grep -A 10 "<resultMap id=\"BaseResultMap\"" src/main/resources/mapper/UserMapper.xml # 看是否有<id column="id" property="id" jdbcType="BIGINT"/>

根因

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询