MyBatis 逆向工程完全指南

发布于 2026-07-30 19:30 更新于 2026-07-30 19:30 12202 字 62 min read ... 访问量

本文全面介绍了 MyBatis 逆向工程的核心概念与实践,涵盖 MyBatis Generator(MBG)和 MyBatis-Plus 代码生成器两大工具。MBG 通过配置文件或代码方式,根据数据库表结构自动生成实体类、Mapper 接口和 XML 映射文件,支持自定义注释、插件和模板;MyBatis-Plus Generator 功能更强大,可一键生成实体、Mapper、Service、Controller 等全栈代码,并支持 Lombok、Swagger 注解和自定义模板。文章详细讲解了配置要点、常见问题排查及最佳实践,强调应将生成代码与自定义逻辑分离,确保可维护性和可重复性。

MyBatis 逆向工程完全指南

本文档全面覆盖 MyBatis 逆向工程(代码生成器)的所有知识点,包括 MyBatis Generator (MBG) 官方工具与 MyBatis-Plus 代码生成器,从配置到实战,无遗漏。

逆向工程概述

什么是逆向工程

MyBatis 逆向工程(Reverse Engineering)是指通过数据库表结构,自动生成 Java 实体类、Mapper 接口、 Mapper XML 映射文件的过程。

数据库表结构  ──→  逆向工程工具  ──→  Java 实体类
                    (Generator)       Mapper 接口
                                      Mapper XML
                                      Example 类
                                      Service(MP)
                                      Controller(MP)

为什么需要逆向工程

传统手写方式逆向工程方式
手动创建实体类,逐个字段映射自动生成,一键完成
手写 CRUD 的 SQL 语句自动生成单表 CRUD
容易拼写错误、遗漏字段与数据库完全一致
新增字段需要手动修改多处重新生成即可
耗时且枯燥秒级完成

主流逆向工程工具对比

工具全称维护方特点
MyBatis Generator (MBG)MyBatis GeneratorMyBatis 官方官方工具,稳定可靠,生成实体+Mapper+XML+Example
MyBatis-Plus GeneratorMP Code GeneratorMyBatis-Plus 社区功能更强,生成实体+Mapper+XML+Service+Controller,支持模板
IDEA 插件(Free MyBatis Tool)EasyCode、MyBatisCodeHelper第三方可视化操作,适合快速开发
RuoYi 代码生成器若依代码生成RuoYi 社区自带模板引擎,生成完整前后端代码

逆向工程能生成什么

MyBatis Generator 生成物:

src/main/java/
  └── com/example/entity/
      ├── User.java              ← 实体类(POJO)
      └── UserExample.java       ← 查询条件类(QBC风格)
  └── com/example/mapper/
      └── UserMapper.java        ← Mapper 接口
src/main/resources/
  └── mapper/
      └── UserMapper.xml         ← SQL 映射文件

MyBatis-Plus Generator 生成物:

 src/main/java/
   └── com/example/
       ├── entity/User.java       ← 实体类(含 Lombok / Swagger 注解)
       ├── mapper/UserMapper.java ← Mapper 接口(继承 BaseMapper)
       ├── service/
       │   ├── UserService.java       ← Service 接口
       │   └── impl/UserServiceImpl.java ← Service 实现
       └── controller/UserController.java ← Controller
 src/main/resources/
  └── mapper/
      └── UserMapper.xml         ← SQL 映射文件(可选)

MyBatis Generator 核心概念

MBG 架构总览

 ┌──────────────────────────────────────────────────┐
 │              MyBatis Generator                    │
 │                                                    │
 │  ┌──────────┐   ┌───────────┐   ┌──────────────┐ │
 │  │ Database  │   │ Generator  │   │  Generated   │ │
 │  │  Metadata │──→│   Engine   │──→│    Code      │ │
 │  │ (JDBC)    │   │ (Java)     │   │  (Java/XML)  │ │
 │  └──────────┘   └─────┬─────┘   └──────────────┘ │
 │                       │                            │
│              ┌────────┴────────┐                   │
│              │  Configuration  │                   │
│              │  (XML / Java)   │                   │
│              └─────────────────┘                   │
└──────────────────────────────────────────────────┘

MBG 工作流程

  1. 读取 generatorConfig.xml 或 Java 配置。

  2. 通过 JDBC 连接数据库并读取表、字段、主键和注释等元数据。

  3. 将 JDBC 类型转换为 Java 类型。

  4. 根据运行模式和模板生成实体类、Mapper 接口、XML 映射文件以及可选的 Example 条件类。

  5. 将生成结果写入指定目录。重新生成前应确认覆盖和合并策略,尤其要注意 XML 文件的合并行为。

MBG 版本说明

MyBatis Generator 使用 1.4.x 版本系列。本文示例采用 1.4.2,版本号用于保证示例可复现,不代表任何时点的“最新版本”。

Maven 坐标使用 org.mybatis.generator:mybatis-generator-core:1.4.2

环境准备与依赖

项目目录结构

 mybatis-generator-demo/
 ├── pom.xml
 ├── src/
 │   ├── main/
 │   │   ├── java/
 │   │   │   └── com/example/
 │   │   │       ├── entity/
 │   │   │       ├── mapper/
 │   │   │       └── generator/
│   │   │           └── Generator.java
│   │   └── resources/
│   │       ├── generatorConfig.xml
│   │       └── mapper/
│   └── test/
└── lib/
    └── mysql-connector-j-8.0.33.jar

Maven 依赖(pom.xml)

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
          http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.example</groupId>
     <artifactId>mybatis-generator-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.16</version>
        </dependency>
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>
        <!-- MyBatis Generator 核心 -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.2</version>
        </dependency>
        <!-- Log4j2(可选,用于日志输出) -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.22.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- MyBatis Generator Maven 插件 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.4.2</version>
                <configuration>
                    <!-- 配置文件路径 -->
                    <configurationFile>
                        src/main/resources/generatorConfig.xml
                    </configurationFile>
                    <!-- 是否覆盖已有文件 -->
                    <overwrite>true</overwrite>
                    <!-- 是否将 MBG 的输出打印到控制台 -->
                    <verbose>true</verbose>
                </configuration>
                <dependencies>
                    <!-- MySQL 驱动(插件也需要) -->
                    <dependency>
                        <groupId>com.mysql</groupId>
                        <artifactId>mysql-connector-j</artifactId>
                        <version>8.0.33</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

示例数据库

 -- 创建数据库
 CREATE DATABASE IF NOT EXISTS mybatis_demo DEFAULT CHARSET utf8mb4;
 USE mybatis_demo;
 -- 部门表
 CREATE TABLE dept (
     dept_id    INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
     dept_name  VARCHAR(50) NOT NULL COMMENT '部门名称',
     location   VARCHAR(100) COMMENT '部门位置'
) COMMENT '部门表';
-- 员工表
CREATE TABLE emp (
    emp_id      INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
    emp_name    VARCHAR(50) NOT NULL COMMENT '员工姓名',
    gender      CHAR(1) DEFAULT 'M' COMMENT '性别 M-男 F-女',
    email       VARCHAR(100) COMMENT '邮箱',
    salary      DECIMAL(10,2) COMMENT '工资',
    dept_id     INT COMMENT '所属部门ID',
    hire_date   DATE COMMENT '入职日期',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT
     '更新时间',
    FOREIGN KEY (dept_id) REFERENCES dept(dept_id)
) COMMENT '员工表';
-- 插入测试数据
INSERT INTO dept VALUES (1, '研发部', '北京'), (2, '市场部', '上海'), (3, '财务部', '广州');
INSERT INTO emp VALUES
(1, '张三', 'M', 'zhangsan@qq.com', 15000.00, 1, '2020-01-15', NOW(), NOW()),
(2, '李四', 'F', 'lisi@qq.com', 12000.00, 2, '2021-03-20', NOW(), NOW()),
(3, '王五', 'M', 'wangwu@qq.com', 18000.00, 1, '2019-07-01', NOW(), NOW());

MBG 三种运行方式

方式一:Java 代码运行(推荐灵活场景)

 package com.example.generator;
 import org.mybatis.generator.api.MyBatisGenerator;
 import org.mybatis.generator.config.xml.ConfigurationParser;
 import org.mybatis.generator.config.Configuration;
 import org.mybatis.generator.internal.DefaultShellCallback;
 import java.io.File;
 import java.util.ArrayList;
import java.util.List;
public class Generator {
    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<>();
        boolean overwrite = true;  // 是否覆盖已有文件
        // 指向配置文件
        File configFile = new File("src/main/resources/generatorConfig.xml");
        ConfigurationParser parser = new ConfigurationParser(warnings);
        Configuration config = parser.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
        // 执行生成
        generator.generate(null);
        // 打印警告信息
        for (String warning : warnings) {
            System.out.println(warning);
        }
        System.out.println("✅ 代码生成完成!");
    }
}

运行方式: 直接右键 main 方法运行。

方式二:Maven 插件运行(推荐常规项目)

pom.xml 中已配置好插件后,执行命令:

# 在项目根目录执行
mvn mybatis-generator:generate
# 如果配置文件不在默认位置,指定路径
mvn mybatis-generator:generate -DconfigurationFile=src/main/resources/generatorConfig.xml
# 如果不想覆盖已有文件
mvn mybatis-generator:generate -Doverwrite=false

注意: Maven 插件方式默认读取 src/main/resources/generatorConfig.xml,如果配置文件放在此路径,无需额外指定。

方式三:命令行运行(适合脚本化)

# 1. 准备 mybatis-generator-core 和数据库驱动
# 2. 准备 generatorConfig.xml
# 3. 执行生成命令
java -jar mybatis-generator-core-1.4.2.jar -configfile generatorConfig.xml -overwrite

# 常用参数:
# -configfile 指定配置文件路径
# -overwrite 允许覆盖可覆盖的 Java 文件
# -verbose 输出详细信息

三种方式对比

特性Java 代码运行Maven 插件命令行
灵活性最高(可编程控制)中等最低
配置复杂度中等
适合场景需要自定义逻辑日常开发CI/CD 脚本
IDE 支持直接运行Maven 面板点击需手动准备 jar

generatorConfig.xml 配置详解

完整配置模板

 <?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"/>
    <!-- ② classPathEntry:指定数据库驱动路径(Maven插件方式可省略) -->
    <classPathEntry location="lib/mysql-connector-j-8.0.33.jar"/>
    <!-- ③ context:生成上下文(可配置多个) -->
    <context id="mysqlContext" targetRuntime="MyBatis3"
     defaultModelType="conditional">
        <!-- ====== 内置插件配置 ====== -->
        <!-- ④ 序列化插件(让实体类实现 Serializable) -->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <!-- ⑤ ToString 插件(生成 toString 方法) -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <!-- ⑥ EqualsAndHashCode 插件 -->
        <plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
        <!-- ⑦ Row Bounds 插件(分页用,MySQL 不推荐) -->
        <!-- <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"/> -->
        <!-- ⑧ 虚拟主键插件 -->
        <!-- <plugin type="org.mybatis.generator.plugins.VirtualKeyPlugin"/> -->
        <!-- ====== 注释生成器 ====== -->
        <!-- ⑨ 自定义注释生成器 -->
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:去除 false:保留 -->
            <property name="suppressAllComments" value="false"/>
            <!-- 是否生成注释中的日期 -->
            <property name="suppressDate" value="true"/>
            <!-- 是否添加数据库表的字段备注 -->
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>
        <!-- ====== 数据库连接 ====== -->
        <!-- ⑩ JDBC 连接配置 -->
         <jdbcConnection
             driverClass="${jdbc.driver}"
             connectionURL="${jdbc.url}"
             userId="${jdbc.username}"
             password="${jdbc.password}">
             <!-- MySQL 8.x 需要设置以下属性,才能获取表注释 -->
             <property name="useInformationSchema" value="true"/>
         </jdbcConnection>
         <!-- ====== 类型解析 ====== -->
         <!-- ⑪ Java 类型解析器 -->
         <javaTypeResolver>
             <!-- 是否强制使用 BigDecimal -->
             <property name="forceBigDecimals" value="false"/>
             <!-- 是否使用 JSR-310 日期类型(java.time) -->
             <property name="useJSR310Types" value="true"/>
         </javaTypeResolver>
         <!-- ====== 代码生成器 ====== -->
         <!-- ⑫ Java 实体类生成器 -->
         <javaModelGenerator
             targetPackage="com.example.entity"
             targetProject="src/main/java">
             <!-- 是否让 schema 作为包的后缀 -->
             <property name="enableSubPackages" value="false"/>
             <!-- 从数据库返回的值是否清理前后的空格 -->
             <property name="trimStrings" value="true"/>
             <!-- 是否为实体类生成构造方法 -->
             <property name="constructorBased" value="false"/>
             <!-- 是否不可变(final 类,全参构造,无 setter) -->
             <property name="immutable" value="false"/>
             <!-- 父类(所有实体类继承的基类) -->
             <!-- <property name="rootClass" value="com.example.BaseEntity"/> -->
         </javaModelGenerator>
         <!-- ⑬ SQL 映射文件生成器 -->
         <sqlMapGenerator
             targetPackage="mapper"
             targetProject="src/main/resources">
             <property name="enableSubPackages" value="false"/>
         </sqlMapGenerator>
         <!-- ⑭ Mapper 接口生成器 -->
         <javaClientGenerator
             type="XMLMAPPER"
             targetPackage="com.example.mapper"
             targetProject="src/main/java">
             <property name="enableSubPackages" value="false"/>
             <!-- 父接口(所有 Mapper 继承的接口) -->
             <!-- <property name="rootInterface" value="com.example.BaseMapper"/> -->
        </javaClientGenerator>
        <!-- ====== 表配置 ====== -->
        <!-- ⑮ table:每张表配置一个 -->
        <table schema="mybatis_demo" tableName="emp"
               domainObjectName="Emp"
               enableCountByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               enableUpdateByExample="true"
               enableDeleteByPrimaryKey="true"
               enableInsert="true"
               enableSelectByPrimaryKey="true"
               enableUpdateByPrimaryKey="true">
            <!-- 主键策略:将自增主键回填到实体 -->
            <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
            <!-- 列覆盖:强制指定某列的 Java 类型 -->
            <!-- <columnOverride column="salary" property="salary"
      javaType="java.math.BigDecimal"/> -->
            <!-- 列忽略:不生成该字段 -->
            <!-- <ignoreColumn column="create_time"/> -->
        </table>
        <table schema="mybatis_demo" tableName="dept"
               domainObjectName="Dept"
               enableCountByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               enableUpdateByExample="true">
            <generatedKey column="dept_id" sqlStatement="MySql" identity="true"/>
        </table>
    </context>
</generatorConfiguration>

外部属性文件(generator.properties)

# generator.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456

context 元素详解

context 属性

<context> 是配置的核心,所有生成规则都在其中定义。

<context id="mysqlContext"
         targetRuntime="MyBatis3"
         defaultModelType="conditional"
         introspectedColumnImpl="org.mybatis.generator.internal.db.DatabaseIntrospector"
         autoDelimitKeywords="false"
         beginningDelimiter="`"
         endingDelimiter="`">
属性说明常用值
id当前上下文的唯一标识任意不重复字符串
targetRuntime选择生成器运行模式MyBatis3MyBatis3SimpleMyBatis3DynamicSql
defaultModelType设置模型类结构conditionalflathierarchical
autoDelimitKeywords是否自动为数据库关键字添加分隔符truefalse
beginningDelimiter关键字起始分隔符MySQL 通常使用反引号
endingDelimiter关键字结束分隔符MySQL 通常使用反引号

targetRuntime 详解

说明生成物
MyBatis3默认,生成完整代码实体 + Example + Mapper + XML(含 Example 方法)
MyBatis3Simple简化版,不生成 Example实体 + Mapper + XML(仅基本 CRUD)
MyBatis3DynamicSql动态 SQL 版(需 MyBatis 3.4.0+)实体 + Mapper(使用 DSL,无 XML)
MyBatis3KotlinKotlin 版Kotlin 实体 + DSL Mapper

MyBatis3Simple 生成的 Mapper 方法(无 Example):

insert()
deleteByPrimaryKey()
updateByPrimaryKey()
selectByPrimaryKey()
selectAll()

MyBatis3 生成的 Mapper 方法(有 Example):

 insert()
 deleteByPrimaryKey()
 updateByPrimaryKey()
 selectByPrimaryKey()
 selectAll() (Simple 没有)
 countByExample()
 deleteByExample()
 selectByExample()
 updateByExampleSelective()
updateByExample()

defaultModelType 详解

说明使用场景
conditional默认。如果表只有一个主键,不生成主键类;有复合主键则生成主键类大多数场景
flat所有表都生成一个扁平的实体类,主键也作为普通字段简单单表
hierarchical生成层次结构:主键类继承→基础实体类→带 BLOB 的类有 BLOB 字段的复杂表

hierarchical 模式生成的类结构:

Emp.java           ← 继承 EmpKey,包含普通字段
EmpKey.java        ← 仅包含主键字段
EmpWithBLOBs.java  ← 继承 Emp,包含 BLOB 字段(如 TEXT/LONGTEXT)

jdbcConnection — 数据库连接

基本配置

<jdbcConnection
    driverClass="com.mysql.cj.jdbc.Driver"
    connectionURL="jdbc:mysql://localhost:3306/mybatis_demo?
    useSSL=false&serverTimezone=Asia/Shanghai"
    userId="root"
    password="123456">
</jdbcConnection>

MySQL 8.x 特殊配置

 <jdbcConnection
     driverClass="com.mysql.cj.jdbc.Driver"
     connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
     userId="root"
     password="123456">
     <!-- ★ 关键:使用 information_schema 获取表/字段注释 -->
     <property name="useInformationSchema" value="true"/>
    <!-- 其他可选属性 -->
    <property name="nullCatalogMeansCurrent" value="true"/>
    <property name="characterEncoding" value="utf-8"/>
    <property name="serverTimezone" value="Asia/Shanghai"/>
</jdbcConnection>

重要: MySQL 8.x 默认不返回表注释和字段注释。必须设置 useInformationSchema=true,否则 commentGenerator 的 addRemarkComments 无效。

各数据库连接配置参考

 <!-- MySQL 8.x -->
 <jdbcConnection
     driverClass="com.mysql.cj.jdbc.Driver"
     connectionURL="jdbc:mysql://localhost:3306/mybatis_demo?
     useSSL=false&amp;serverTimezone=Asia/Shanghai"
     userId="root" password="123456">
     <property name="useInformationSchema" value="true"/>
 </jdbcConnection>
 <!-- PostgreSQL -->
<jdbcConnection
    driverClass="org.postgresql.Driver"
    connectionURL="jdbc:postgresql://localhost:5432/mydb"
    userId="postgres" password="123456"/>
<!-- Oracle -->
<jdbcConnection
    driverClass="oracle.jdbc.OracleDriver"
    connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
    userId="scott" password="tiger"/>
<!-- SQL Server -->
<jdbcConnection
    driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
    connectionURL="jdbc:sqlserver://localhost:1433;databaseName=mydb"
    userId="sa" password="123456"/>

javaModelGenerator — 实体类生成

基本配置

 <javaModelGenerator
     targetPackage="com.example.entity"
     targetProject="src/main/java">
     <!-- 是否让 schema 作为包名后缀 -->
     <!-- true: com.example.entity.mybatis_demo.Emp -->
     <!-- false: com.example.entity.Emp -->
     <property name="enableSubPackages" value="false"/>
    <!-- 是否清理从数据库返回的字符串前后的空格 -->
    <property name="trimStrings" value="true"/>
    <!-- 是否基于构造方法(生成全参构造 + setter) -->
    <property name="constructorBased" value="false"/>
    <!-- 是否生成不可变类(final + 全参构造 + 无 setter) -->
    <property name="immutable" value="false"/>
    <!-- 所有实体类的父类 -->
    <!-- <property name="rootClass" value="com.example.BaseEntity"/> -->
</javaModelGenerator>

生成的实体类示例

配置: targetPackage=“com.example.entity”, trimStrings=“true”

 package com.example.entity;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 public class Emp {
     private Integer empId;
    private String empName;
    private String gender;
    private String email;
    private BigDecimal salary;
    private Integer deptId;
    private LocalDate hireDate;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    // ====== 构造方法 ======
    public Emp(Integer empId, String empName, String gender, String email,
               BigDecimal salary, Integer deptId, LocalDate hireDate,
               LocalDateTime createTime, LocalDateTime updateTime) {
        this.empId = empId;
        this.empName = empName == null ? null : empName.trim();
        this.gender = gender;
        this.email = email == null ? null : email.trim();
        this.salary = salary;
        this.deptId = deptId;
        this.hireDate = hireDate;
        this.createTime = createTime;
        this.updateTime = updateTime;
    }
    public Emp() {
    }
    // ====== getter / setter ======
    public Integer getEmpId() {
        return empId;
    }
    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName == null ? null : empName.trim();
    }
    // ... 其他 getter/setter 省略 ...
}

rootClass 示例

// BaseEntity.java — 所有实体类的父类
package com.example.entity;
import java.time.LocalDateTime;
public abstract class BaseEntity {
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    // getter / setter
    public LocalDateTime getCreateTime() { return createTime; }
    public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime;
     }
    public LocalDateTime getUpdateTime() { return updateTime; }
    public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime;
     }
}

配置 rootClass 后,create_time 和 update_time 字段不会被重复生成在子类中。

sqlMapGenerator — XML 映射文件生成

基本配置

<sqlMapGenerator
    targetPackage="mapper"
    targetProject="src/main/resources">
    <property name="enableSubPackages" value="false"/>
</sqlMapGenerator>

生成的 XML 示例(EmpMapper.xml)

 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.example.mapper.EmpMapper">
     <resultMap id="BaseResultMap" type="com.example.entity.Emp">
         <id column="emp_id" property="empId" jdbcType="INTEGER"/>
         <result column="emp_name" property="empName" jdbcType="VARCHAR"/>
         <result column="gender" property="gender" jdbcType="CHAR"/>
        <result column="email" property="email" jdbcType="VARCHAR"/>
        <result column="salary" property="salary" jdbcType="DECIMAL"/>
        <result column="dept_id" property="deptId" jdbcType="INTEGER"/>
        <result column="hire_date" property="hireDate" jdbcType="DATE"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
    </resultMap>
    <!-- 通用列名 -->
    <sql id="Base_Column_List">
        emp_id, emp_name, gender, email, salary, dept_id, hire_date, create_time,
        update_time
    </sql>
    <!-- 按主键查询 -->
    <select id="selectByPrimaryKey" parameterType="java.lang.Integer"
            resultMap="BaseResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM emp
        WHERE emp_id = #{empId,jdbcType=INTEGER}
    </select>
    <!-- 删除 -->
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
        DELETE FROM emp
        WHERE emp_id = #{empId,jdbcType=INTEGER}
    </delete>
    <!-- 插入 -->
    <insert id="insert" parameterType="com.example.entity.Emp"
            useGeneratedKeys="true" keyProperty="empId">
        INSERT INTO emp (emp_name, gender, email,
                         salary, dept_id, hire_date,
                         create_time, update_time)
        VALUES (#{empName,jdbcType=VARCHAR}, #{gender,jdbcType=CHAR},
                #{email,jdbcType=VARCHAR}, #{salary,jdbcType=DECIMAL},
                #{deptId,jdbcType=INTEGER}, #{hireDate,jdbcType=DATE},
                #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP})
    </insert>
    <!-- 选择性插入(null 字段不插入) -->
    <insert id="insertSelective" parameterType="com.example.entity.Emp"
            useGeneratedKeys="true" keyProperty="empId">
        INSERT INTO emp
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="empName != null">emp_name,</if>
            <if test="gender != null">gender,</if>
            <if test="email != null">email,</if>
            <if test="salary != null">salary,</if>
            <if test="deptId != null">dept_id,</if>
            <if test="hireDate != null">hire_date,</if>
            <if test="createTime != null">create_time,</if>
            <if test="updateTime != null">update_time,</if>
        </trim>
        <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
            <if test="empName != null">#{empName,jdbcType=VARCHAR},</if>
            <if test="gender != null">#{gender,jdbcType=CHAR},</if>
            <if test="email != null">#{email,jdbcType=VARCHAR},</if>
            <if test="salary != null">#{salary,jdbcType=DECIMAL},</if>
            <if test="deptId != null">#{deptId,jdbcType=INTEGER},</if>
            <if test="hireDate != null">#{hireDate,jdbcType=DATE},</if>
            <if test="createTime != null">#{createTime,jdbcType=TIMESTAMP},</if>
            <if test="updateTime != null">#{updateTime,jdbcType=TIMESTAMP},</if>
        </trim>
    </insert>
    <!-- 选择性更新 -->
     <update id="updateByPrimaryKeySelective" parameterType="com.example.entity.Emp">
         UPDATE emp
         <set>
             <if test="empName != null">emp_name = #{empName,jdbcType=VARCHAR},</if>
             <if test="gender != null">gender = #{gender,jdbcType=CHAR},</if>
             <if test="email != null">email = #{email,jdbcType=VARCHAR},</if>
             <if test="salary != null">salary = #{salary,jdbcType=DECIMAL},</if>
             <if test="deptId != null">dept_id = #{deptId,jdbcType=INTEGER},</if>
             <if test="hireDate != null">hire_date = #{hireDate,jdbcType=DATE},</if>
             <if test="createTime != null">create_time = #
      {createTime,jdbcType=TIMESTAMP},</if>
             <if test="updateTime != null">update_time = #
      {updateTime,jdbcType=TIMESTAMP},</if>
         </set>
         WHERE emp_id = #{empId,jdbcType=INTEGER}
     </update>
     <!-- 全量更新 -->
     <update id="updateByPrimaryKey" parameterType="com.example.entity.Emp">
         UPDATE emp
         SET emp_name    = #{empName,jdbcType=VARCHAR},
             gender      = #{gender,jdbcType=CHAR},
             email       = #{email,jdbcType=VARCHAR},
             salary      = #{salary,jdbcType=DECIMAL},
            dept_id     = #{deptId,jdbcType=INTEGER},
            hire_date   = #{hireDate,jdbcType=DATE},
            create_time = #{createTime,jdbcType=TIMESTAMP},
            update_time = #{updateTime,jdbcType=TIMESTAMP}
        WHERE emp_id = #{empId,jdbcType=INTEGER}
    </update>
</mapper>

javaClientGenerator — Mapper 接口生成

基本配置

<javaClientGenerator
    type="XMLMAPPER"
    targetPackage="com.example.mapper"
    targetProject="src/main/java">
    <property name="enableSubPackages" value="false"/>
</javaClientGenerator>

type 属性

说明生成物
XMLMAPPER接口与 XML 映射文件分离,适合大多数项目Mapper 接口与对应 XML
ANNOTATEDMAPPERSQL 主要通过注解写在接口中Mapper 接口,无独立 XML
MIXEDMAPPER基础语句使用注解,复杂语句保留 XMLMapper 接口与部分 XML

生成的 Mapper 接口示例(XMLMAPPER 模式)

 package com.example.mapper;
 import com.example.entity.Emp;
 import com.example.entity.EmpExample;
 import java.util.List;
 import org.apache.ibatis.annotations.Param;
 public interface EmpMapper {
    // ====== 按 Example 查询 ======
    long countByExample(EmpExample example);
    int deleteByExample(EmpExample example);
    List<Emp> selectByExample(EmpExample example);
    int updateByExampleSelective(@Param("record") Emp record,
                                  @Param("example") EmpExample example);
    int updateByExample(@Param("record") Emp record,
                         @Param("example") EmpExample example);
    // ====== 按主键操作 ======
    int deleteByPrimaryKey(Integer empId);
    int insert(Emp record);
    int insertSelective(Emp record);
    Emp selectByPrimaryKey(Integer empId);
    int updateByPrimaryKeySelective(Emp record);
    int updateByPrimaryKey(Emp record);
}

ANNOTATEDMAPPER 模式生成的接口

// 纯注解,没有 XML 文件
public interface EmpMapper {
    @Insert({
        "INSERT INTO emp (emp_name, gender, ...)",
        "VALUES (#{empName,jdbcType=VARCHAR}, #{gender,jdbcType=CHAR}, ...)"
    })
     @Options(useGeneratedKeys = true, keyProperty = "empId")
     int insert(Emp record);
    @Select({
        "SELECT emp_id, emp_name, gender, ...",
        "FROM emp",
        "WHERE emp_id = #{empId,jdbcType=INTEGER}"
    })
    @ResultMap("BaseResultMap")
    Emp selectByPrimaryKey(Integer empId);
    // ... 其他方法
}

table 元素详解

table 完整属性

 <table
     schema="mybatis_demo"           <!-- 数据库名/schema -->
     tableName="emp"                  <!-- 数据库表名(支持通配符 %) -->
     domainObjectName="Emp"           <!-- 生成的实体类名 -->
     mapperName="EmpMapper"           <!-- 生成的 Mapper 接口名(可含包名) -->
     sqlMapName="EmpMapper"           <!-- 生成的 XML 文件名 -->
     alias="e"                        <!-- 表别名(用于多表查询) -->
     enableCountByExample="true"      <!-- 是否生成 countByExample -->
     enableDeleteByExample="true"     <!-- 是否生成 deleteByExample -->
    enableSelectByExample="true"     <!-- 是否生成 selectByExample -->
    enableUpdateByExample="true"     <!-- 是否生成 updateByExample -->
    enableDeleteByPrimaryKey="true"  <!-- 是否生成 deleteByPrimaryKey -->
    enableInsert="true"              <!-- 是否生成 insert -->
    enableSelectByPrimaryKey="true"  <!-- 是否生成 selectByPrimaryKey -->
    enableUpdateByPrimaryKey="true"  <!-- 是否生成 updateByPrimaryKey -->
    selectByPrimaryKeyQueryId="false"
    selectByExampleQueryId="false"
    modelType="conditional"          <!-- 覆盖 context 的 defaultModelType -->
    delimitIdentifiers="false"       <!-- 是否给所有标识符加分隔符 -->
    delimitAllColumns="false"        <!-- 是否给所有列加分隔符 -->
>

tableName 通配符用法

 <!-- 生成所有表(慎用) -->
 <table tableName="%"/>
 <!-- 生成所有以 t_ 开头的表 -->
 <table tableName="t_%" />
 <!-- 排除某些表(需配合 table 的 schema 属性) -->
 <table tableName="%" >
     <!-- 通过 domainObjectRenamingRule 来调整命名 -->
</table>

generatedKey — 主键回填

 <table tableName="emp" domainObjectName="Emp">
     <!-- 方式一:MySQL 自增主键 -->
     <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
     <!-- 方式二:SQL Server 自增主键 -->
     <!-- <generatedKey column="id" sqlStatement="SQL Server" identity="true"/> -->
     <!-- 方式三:Oracle 序列 -->
     <!-- <generatedKey column="id" sqlStatement="select SEQ_EMP.nextval from dual"
     identity="false"/> -->
    <!-- 方式四:PostgreSQL -->
    <!-- <generatedKey column="id" sqlStatement="PostgreSQL" identity="true"/> -->
</table>
属性说明
column主键列名
sqlStatement获取主键值的语句或数据库标识,例如 MySQL 常用 MySql
identitytrue 表示数据库自增主键;false 表示由语句取得主键值
typepre 表示插入前获取主键,post 表示插入后获取主键

columnOverride — 列覆盖

<table tableName="emp" domainObjectName="Emp">
    <!-- 强制指定某列的 Java 类型和 jdbcType -->
    <columnOverride column="salary"
                    property="salary"
                    javaType="java.math.BigDecimal"
                    jdbcType="DECIMAL"/>
    <!-- 强制指定枚举类型 -->
     <columnOverride column="gender"
                    property="gender"
                    javaType="com.example.enums.Gender"
                    typeHandler="com.example.handler.GenderTypeHandler"/>
    <!-- 修改属性名(驼峰自定义) -->
    <columnOverride column="emp_name" property="name"/>
    <!-- 强制 BLOB 类型 -->
    <columnOverride column="content" isBlob="true" javaType="java.lang.String"/>
</table>

ignoreColumn — 忽略列

<table tableName="emp" domainObjectName="Emp">
    <!-- 不生成这些字段 -->
    <ignoreColumn column="create_time"/>
    <ignoreColumn column="update_time"/>
    <!-- 支持正则匹配 -->
    <!-- <ignoreColumn pattern="^temp_.*"/> -->
</table>

domainObjectRenamingRule — 实体类重命名规则

<table tableName="t_emp" domainObjectName="Emp">
    <!-- 去掉表名前缀 t_ -->
    <domainObjectRenamingRule searchString="^T_" replaceString=""/>
</table>

columnRenamingRule — 列重命名规则

<table tableName="emp">
    <!-- 去掉列名前缀 F_ -->
    <columnRenamingRule searchString="^F_" replaceString=""/>
</table>

生成的代码结构说明

完整目录结构

mybatis-generator-demo/
├── src/main/java/com/example/
│   ├── entity/
│   │   ├── Emp.java              ← 员工实体类
│   │   ├── EmpExample.java       ← 员工查询条件类
│   │   ├── Dept.java             ← 部门实体类
│   │   └── DeptExample.java      ← 部门查询条件类
│   ├── mapper/
 │   │   ├── EmpMapper.java        ← 员工 Mapper 接口
│   │   └── DeptMapper.java       ← 部门 Mapper 接口
│   └── generator/
│       └── Generator.java        ← 生成器启动类
├── src/main/resources/
│   ├── generatorConfig.xml       ← MBG 配置文件
│   ├── generator.properties      ← 属性文件
│   ├── mapper/
│   │   ├── EmpMapper.xml         ← 员工 SQL 映射
│   │   └── DeptMapper.xml        ← 部门 SQL 映射
│   └── mybatis-config.xml        ← MyBatis 主配置
└── pom.xml

各方法功能对照表

方法功能对应 SQL

INSERT INTO emp (…) VALUES insert(Emp record)插入全部字段(…)

insertSelective(Emp record)

选择性插入(null 字 INSERT INTO emp (…) VALUES 段不插入)(…) +

DELETE FROM emp WHERE emp_id deleteByPrimaryKey(Integer id)按主键删除= ?

deleteByExample(EmpExample example)按条件删除 DELETE FROM emp WHERE …

UPDATE emp SET … WHERE updateByPrimaryKey(Emp record)全量更新 emp_id = ?

updateByPrimaryKeySelective(EmpUPDATE emp SET … WHERE 选择性更新 record)emp_id = ? +

updateByExample(…)按条件全量更新 UPDATE emp SET … WHERE …

 UPDATE emp SET ... WHERE ...
+ <if>

updateByExampleSelective(…)按条件选择性更新

SELECT … FROM emp WHERE selectByPrimaryKey(Integer id)按主键查询 emp_id = ?

 SELECT ... FROM emp WHERE
...

selectByExample(EmpExample example)按条件查询

SELECT COUNT(*) FROM emp countByExample(EmpExample example)按条件计数 WHERE …

Example 类详解(QBC 风格查询)

Example 类的作用

Example 类是 MBG 自动生成的查询条件构造器,采用 QBC(Query By Criteria) 风格,通过链式调用构建 WHERE 条件,避免手写 SQL。

Example 类结构

 public class EmpExample {
     protected String orderByClause;          // 排序字段
     protected boolean distinct;              // 是否去重
     protected List<Criteria> oredCriteria;   // OR 条件列表
     // 设置排序
     public void setOrderByClause(String orderByClause) { ... }
     // 设置去重
    public void setDistinct(boolean distinct) { ... }
    // 获取 OR 条件列表
    public List<Criteria> getOredCriteria() { ... }
    // 创建一个 OR 条件(相当于 OR (...))
    public Criteria or() { ... }
    // 添加一个 Criteria 到 OR 列表
    public void or(Criteria criteria) { ... }
    // 创建一个 Criteria(相当于一个 AND 条件组)
    public Criteria createCriteria() { ... }
    // = createCriteria(),内部调用
    protected Criteria createCriteriaInternal() { ... }
    // 清空所有条件
    public void clear() { ... }
    // ====== Criteria 内部类 ======
    public abstract static class GeneratedCriteria {
        // 等于
        public Criteria andEmpIdEqualTo(Integer value) { ... }
        // 不等于
        public Criteria andEmpIdNotEqualTo(Integer value) { ... }
        // 大于
        public Criteria andEmpIdGreaterThan(Integer value) { ... }
        // 大于等于
        public Criteria andEmpIdGreaterThanOrEqualTo(Integer value) { ... }
        // 小于
        public Criteria andEmpIdLessThan(Integer value) { ... }
        // 小于等于
        public Criteria andEmpIdLessThanOrEqualTo(Integer value) { ... }
        // IN
        public Criteria andEmpIdIn(List<Integer> values) { ... }
        // NOT IN
        public Criteria andEmpIdNotIn(List<Integer> values) { ... }
        // BETWEEN
        public Criteria andEmpIdBetween(Integer value1, Integer value2) { ... }
        // NOT BETWEEN
        public Criteria andEmpIdNotBetween(Integer value1, Integer value2) { ... }
        // LIKE
        public Criteria andEmpNameLike(String value) { ... }
        // NOT LIKE
        public Criteria andEmpNameNotLike(String value) { ... }
        // IS NULL
        public Criteria andEmpNameIsNull() { ... }
        // IS NOT NULL
        public Criteria andEmpNameIsNotNull() { ... }
        // ... 每个字段都有以上方法
    }
    // ====== Criteria 继承类 ======
    public static class Criteria extends GeneratedCriteria {
        // ...
    }
}

Example 常用查询示例

 // ====== 1. 查询所有 ======
 List<Emp> allEmps = empMapper.selectByExample(new EmpExample());
 // ====== 2. 按主键查询 ======
 Emp emp = empMapper.selectByPrimaryKey(1);
 // ====== 3. 等值查询:gender = 'M' ======
 EmpExample example = new EmpExample();
 example.createCriteria().andGenderEqualTo("M");
List<Emp> males = empMapper.selectByExample(example);
// ====== 4. 多条件 AND 查询 ======
// WHERE gender = 'M' AND salary > 10000 AND dept_id = 1
EmpExample example = new EmpExample();
example.createCriteria()
        .andGenderEqualTo("M")
        .andSalaryGreaterThan(new BigDecimal("10000"))
        .andDeptIdEqualTo(1);
List<Emp> result = empMapper.selectByExample(example);
// ====== 5. OR 条件查询 ======
// WHERE (gender = 'M' AND salary > 15000) OR (gender = 'F' AND salary > 12000)
EmpExample example = new EmpExample();
example.createCriteria()
        .andGenderEqualTo("M")
        .andSalaryGreaterThan(new BigDecimal("15000"));
example.or()
        .andGenderEqualTo("F")
        .andSalaryGreaterThan(new BigDecimal("12000"));
List<Emp> result = empMapper.selectByExample(example);
// ====== 6. LIKE 查询 ======
// WHERE emp_name LIKE '%张%'
EmpExample example = new EmpExample();
example.createCriteria().andEmpNameLike("%张%");
List<Emp> result = empMapper.selectByExample(example);
// ====== 7. IN 查询 ======
// WHERE dept_id IN (1, 2, 3)
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdIn(Arrays.asList(1, 2, 3));
List<Emp> result = empMapper.selectByExample(example);
// ====== 8. BETWEEN 查询 ======
// WHERE salary BETWEEN 8000 AND 20000
EmpExample example = new EmpExample();
example.createCriteria()
        .andSalaryBetween(new BigDecimal("8000"), new BigDecimal("20000"));
List<Emp> result = empMapper.selectByExample(example);
// ====== 9. IS NULL 查询 ======
// WHERE email IS NULL
EmpExample example = new EmpExample();
example.createCriteria().andEmailIsNull();
List<Emp> result = empMapper.selectByExample(example);
// ====== 10. 排序 ======
// ORDER BY salary DESC
EmpExample example = new EmpExample();
example.setOrderByClause("salary DESC");
List<Emp> result = empMapper.selectByExample(example);
// ====== 11. 去重 ======
EmpExample example = new EmpExample();
example.setDistinct(true);
example.setOrderByClause("dept_id");
List<Emp> result = empMapper.selectByExample(example);
// ====== 12. 条件删除 ======
// DELETE FROM emp WHERE dept_id = 3
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdEqualTo(3);
empMapper.deleteByExample(example);
// ====== 13. 条件更新 ======
// UPDATE emp SET salary = 20000 WHERE dept_id = 1
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdEqualTo(1);
Emp record = new Emp();
record.setSalary(new BigDecimal("20000"));
empMapper.updateByExampleSelective(record, example);
// ====== 14. 条件计数 ======
EmpExample example = new EmpExample();
example.createCriteria().andGenderEqualTo("M");
long count = empMapper.countByExample(example);

Example 使用对照表

需求Example 方法SQL 操作符
等于andXxxEqualTo(value)=
不等于andXxxNotEqualTo(value)<>
大于andXxxGreaterThan(value)>
大于等于andXxxGreaterThanOrEqualTo(value)>=
小于andXxxLessThan(value)<
小于等于andXxxLessThanOrEqualTo(value)<=
模糊匹配andXxxLike(value)LIKE
非模糊匹配andXxxNotLike(value)NOT LIKE
集合包含andXxxIn(list)IN
集合不包含andXxxNotIn(list)NOT IN
区间匹配andXxxBetween(v1, v2)BETWEEN
为空andXxxIsNull()IS NULL
非空andXxxIsNotNull()IS NOT NULL
添加 OR 条件组example.or().andXxx...()OR
排序example.setOrderByClause("xxx DESC")ORDER BY
去重example.setDistinct(true)DISTINCT

自定义 CommentGenerator(注释生成器)

默认注释的问题

MBG 默认生成的注释格式:

/**
 * This field was generated by MyBatis Generator.
 * This field corresponds to the database column emp.emp_name
 *
 * @mbg.generated
 */
private String empName;

问题:注释无用、没有数据库字段注释、没有日期格式。

自定义 CommentGenerator

 package com.example.generator;
 import org.mybatis.generator.api.IntrospectedColumn;
 import org.mybatis.generator.api.IntrospectedTable;
 import org.mybatis.generator.api.dom.java.*;
 import org.mybatis.generator.api.dom.xml.XmlElement;
 import org.mybatis.generator.internal.DefaultCommentGenerator;
 import java.util.Properties;
/**
 * 自定义注释生成器
 * 生成数据库字段注释到 Java 实体类中
 */
public class CustomCommentGenerator extends DefaultCommentGenerator {
    private boolean addRemarkComments;
    @Override
    public void addConfigurationProperties(Properties properties) {
        super.addConfigurationProperties(properties);
        this.addRemarkComments = Boolean.parseBoolean(
            properties.getProperty("addRemarkComments"));
    }
    /**
     * 给字段添加注释(数据库列注释)
     */
    @Override
    public void addFieldComment(Field field,
                                 IntrospectedTable introspectedTable,
                                 IntrospectedColumn introspectedColumn) {
        if (addRemarkComments) {
            String remark = introspectedColumn.getRemarks();
            if (remark != null && !remark.trim().isEmpty()) {
                field.addJavaDocLine("/**");
                field.addJavaDocLine(" * " + remark);
                field.addJavaDocLine(" */");
            }
        }
    }
    /**
     * 给类添加注释(表注释)
     */
    @Override
    public void addClassComment(InnerClass innerClass,
                                 IntrospectedTable introspectedTable) {
        String remark = introspectedTable.getRemarks();
        innerClass.addJavaDocLine("/**");
        if (remark != null && !remark.trim().isEmpty()) {
            innerClass.addJavaDocLine(" * " + remark);
        }
        innerClass.addJavaDocLine(" * 对应数据库表: " +
            introspectedTable.getFullyQualifiedTable());
        innerClass.addJavaDocLine(" */");
    }
    /**
     * 给 getter 方法添加注释
     */
    @Override
    public void addGetterComment(Method method,
                                  IntrospectedTable introspectedTable,
                                  IntrospectedColumn introspectedColumn) {
        // 不生成 getter 注释(保持简洁)
    }
    /**
     * 给 setter 方法添加注释
     */
    @Override
    public void addSetterComment(Method method,
                                  IntrospectedTable introspectedTable,
                                  IntrospectedColumn introspectedColumn) {
        // 不生成 setter 注释(保持简洁)
    }
}

在配置文件中使用自定义注释生成器

 <context id="mysqlContext" targetRuntime="MyBatis3">
     <!-- 使用自定义注释生成器 -->
     <commentGenerator type="com.example.generator.CustomCommentGenerator">
         <property name="suppressAllComments" value="false"/>
         <property name="suppressDate" value="true"/>
         <property name="addRemarkComments" value="true"/>
     </commentGenerator>
    <!-- ... 其他配置 ... -->
</context>

生成的实体类效果

 /**
  * 员工表
  * 对应数据库表: mybatis_demo.emp
  */
 public class Emp {
     /**
      * 员工ID
      */
    private Integer empId;
    /**
     * 员工姓名
     */
    private String empName;
    /**
     * 性别 M-男 F-女
     */
    private String gender;
    // ... getter/setter
}

自定义 Plugin 插件

Plugin 机制概述

MBG 通过 Plugin 插件机制 实现代码生成过程的扩展。插件可以拦截生成过程,修改或增加生成的代码。

 MBG 生成流程

 ┌─────────────────────────────────┐
 │  阶段1: Model Class 生成         │ ← Plugin 可拦截
 ├─────────────────────────────────┤
 │  阶段2: SQL Map (XML) 生成       │ ← Plugin 可拦截
 ├─────────────────────────────────┤
 │  阶段3: Client (Mapper) 生成     │ ← Plugin 可拦截
 ├─────────────────────────────────┤
│  阶段4: Example Class 生成       │ ← Plugin 可拦截
└─────────────────────────────────┘

内置插件列表

插件类功能
SerializablePlugin让实体类实现 Serializable 接口
ToStringPlugin生成 toString() 方法
EqualsAndHashCodePlugin生成 equals() 和 hashCode() 方法
RowBoundsPlugin生成分页方法(基于 RowBounds,不推荐 MySQL 使用)
VirtualKeyPlugin虚拟主键插件
FluentBuilderMethodsPlugin生成链式 setter(返回 this)
MapperNamePlugin自定义 Mapper 名称
RenameExampleClassPlugin重命名 Example 类
CaseInsensitiveLikePlugin大小写不敏感 LIKE 查询
UnmergeableXmlMapperPluginXML 不合并,直接覆盖

自定义 Plugin 示例:Lombok 插件

让生成的实体类使用 Lombok 注解,替代冗长的 getter/setter:

 package com.example.generator;
 import org.mybatis.generator.api.IntrospectedTable;
 import org.mybatis.generator.api.PluginAdapter;
 import org.mybatis.generator.api.dom.java.*;
 import java.util.List;
 /**
 * 自定义 Lombok 插件
 * 让生成的实体类使用 @Data 注解,去掉 getter/setter
 */
public class LombokPlugin extends PluginAdapter {
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
    /**
     * 给实体类添加 Lombok 注解
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
                                                  IntrospectedTable introspectedTable)
     {
        // 添加 @Data 注解
        topLevelClass.addImportedType("lombok.Data");
        topLevelClass.addAnnotation("@Data");
        // 添加 @Builder 注解(可选)
        topLevelClass.addImportedType("lombok.Builder");
        topLevelClass.addAnnotation("@Builder");
        // 添加 @NoArgsConstructor
        topLevelClass.addImportedType("lombok.NoArgsConstructor");
        topLevelClass.addAnnotation("@NoArgsConstructor");
        // 添加 @AllArgsConstructor
        topLevelClass.addImportedType("lombok.AllArgsConstructor");
        topLevelClass.addAnnotation("@AllArgsConstructor");
        return true;
    }
    /**
     * 给主键类添加 Lombok 注解(如果有)
     */
    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass,
                                                  IntrospectedTable introspectedTable)
     {
        topLevelClass.addImportedType("lombok.Data");
        topLevelClass.addAnnotation("@Data");
        return true;
    }
    /**
     * 禁止生成 getter 方法(由 Lombok @Data 提供)
     */
    @Override
    public boolean modelGetterMethodGenerated(Method method,
                                               TopLevelClass topLevelClass,
                                               IntrospectedColumn introspectedColumn,
                                               IntrospectedTable introspectedTable,
                                               ModelClassType modelClassType) {
        return false;  // false = 不生成 getter
    }
    /**
     * 禁止生成 setter 方法
     */
    @Override
    public boolean modelSetterMethodGenerated(Method method,
                                               TopLevelClass topLevelClass,
                                               IntrospectedColumn introspectedColumn,
                                               IntrospectedTable introspectedTable,
                                               ModelClassType modelClassType) {
        return false;  // false = 不生成 setter
    }
}

配置中使用自定义插件

 <context id="mysqlContext" targetRuntime="MyBatis3">
     <!-- 内置插件 -->
     <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
     <plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
     <!-- ★ 自定义 Lombok 插件 -->
     <plugin type="com.example.generator.LombokPlugin"/>
    <!-- ★ 重命名 Example 类为 Query -->
    <plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
        <property name="searchString" value="Example$"/>
        <property name="replaceString" value="Query"/>
    </plugin>
    <!-- 自定义注释生成器 -->
    <commentGenerator type="com.example.generator.CustomCommentGenerator">
        <property name="suppressAllComments" value="false"/>
        <property name="addRemarkComments" value="true"/>
    </commentGenerator>
    <!-- ... 其他配置 ... -->
</context>

自定义 Plugin 示例:Mapper 继承通用接口

package com.example.generator;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
 import org.mybatis.generator.api.dom.java.TopLevelClass;
 import java.util.List;
/**
 * 让所有生成的 Mapper 接口继承自定义的 BaseMapper
 */
public class BaseMapperPlugin extends PluginAdapter {
    private String baseMapperPackage;
    @Override
    public boolean validate(List<String> warnings) {
        baseMapperPackage = properties.getProperty("baseMapperPackage");
        return baseMapperPackage != null && !baseMapperPackage.trim().isEmpty();
    }
    @Override
    public boolean clientGenerated(Interface interfaze,
                                    TopLevelClass topLevelClass,
                                    IntrospectedTable introspectedTable) {
        // 导入 BaseMapper
        FullyQualifiedJavaType baseMapperType =
            new FullyQualifiedJavaType(baseMapperPackage);
        interfaze.addImportedType(baseMapperType);
        // 让 Mapper 继承 BaseMapper<实体类>
        FullyQualifiedJavaType entityType =
            new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
        interfaze.addSuperInterface(
            new FullyQualifiedJavaType(baseMapperPackage + "<" +
     entityType.getShortName() + ">"));
        return true;
    }
}
<plugin type="com.example.generator.BaseMapperPlugin">
    <property name="baseMapperPackage" value="com.example.mapper.BaseMapper"/>
</plugin>

MyBatis-Plus 代码生成器(3.5.x+)

MP Generator 概述

MyBatis-Plus 代码生成器比 MBG 功能更强大,能生成:

  • Entity 实体类(支持 Lombok / Swagger 注解)
  • Mapper 接口(继承 BaseMapper)
  • Mapper XML 映射文件
  • Service 接口 + Service 实现类
  • Controller 控制器(含 RESTful 接口)

依赖配置

 <dependencies>
     <!-- MyBatis-Plus(包含 MyBatis) -->
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>3.5.7</version>
     </dependency>
     <!-- MP 代码生成器核心 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.7</version>
    </dependency>
    <!-- 模板引擎(Freemarker / Velocity / Beetl 任选其一) -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.32</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!-- Lombok(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.34</version>
        <scope>provided</scope>
    </dependency>
    <!-- Swagger / SpringDoc(可选) -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.5.0</version>
    </dependency>
</dependencies>

快速生成器(FastAutoGenerator)

MyBatis-Plus 3.5.3+ 提供了 FastAutoGenerator,链式配置一行搞定:

 package com.example.generator;
 import com.baomidou.mybatisplus.generator.FastAutoGenerator;
 import com.baomidou.mybatisplus.generator.config.OutputFile;
 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
 import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
 import java.util.Collections;
public class MpGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create(
                "jdbc:mysql://localhost:3306/mybatis_demo?
     useSSL=false&serverTimezone=Asia/Shanghai",
                "root",
                "123456")
            // ====== 全局配置 ======
            .globalConfig(builder -> {
                builder.author("example")               // 作者
                       .outputDir("src/main/java")      // 输出目录
                       .enableSwagger()                  // 开启 Swagger 注解
                       .commentDate("yyyy-MM-dd");       // 注释日期格式
            })
            // ====== 包配置 ======
            .packageConfig(builder -> {
                builder.parent("com.example")           // 父包名
                       .entity("entity")                // 实体类包名
                       .mapper("mapper")                // Mapper 包名
                       .service("service")              // Service 包名
                       .serviceImpl("service.impl")     // Service 实现包名
                       .controller("controller")        // Controller 包名
                       .xml("mapper")                   // XML 资源路径
                       .pathInfo(Collections.singletonMap(
                           OutputFile.xml,
                           "src/main/resources/mapper")); // XML 输出目录
            })
            // ====== 策略配置 ======
            .strategyConfig(builder -> {
                builder.addInclude("emp", "dept")       // 表名(可变参数)
                       .addTablePrefix("t_", "sys_")    // 过滤表前缀
                       .entityBuilder()
                       .naming(NamingStrategy.underline_to_camel)  // 下划线转驼峰
                       .columnNaming(NamingStrategy.underline_to_camel)
                       .enableLombok()                   // 使用 Lombok
                       .enableTableFieldAnnotation()     // 生成字段注解
                       .logicDeleteColumnName("deleted") // 逻辑删除字段
                       .versionColumnName("version")     // 乐观锁字段
                       .idType(IdType.AUTO)              // 主键策略
                       .formatFileName("%s")             // 实体类名格式
                       .mapperBuilder()
                       .enableMapperAnnotation()          // 生成 @Mapper 注解
                       .formatMapperFileName("%sMapper")   // Mapper 文件名格式
                       .formatXmlFileName("%sMapper")      // XML 文件名格式
                       .serviceBuilder()
                       .formatServiceFileName("%sService")     // Service 接口名
                       .formatServiceImplFileName("%sServiceImpl") // Service 实现名
                       .controllerBuilder()
                       .enableRestStyle();                // 生成 @RestController
            })
            // ====== 模板引擎 ======
            .templateEngine(new FreemarkerTemplateEngine())
            // ====== 执行 ======
            .execute();
        System.out.println("✅ MyBatis-Plus 代码生成完成!");
    }
}

生成的目录结构

 src/main/java/com/example/
 ├── entity/
 │   ├── Emp.java              ← @Data + @TableName + 字段注解
 │   └── Dept.java
 ├── mapper/
 │   ├── EmpMapper.java        ← extends BaseMapper<Emp>
 │   └── DeptMapper.java
 ├── service/
 │   ├── EmpService.java       ← extends IService<Emp>
│   └── impl/
│       └── EmpServiceImpl.java  ← extends ServiceImpl<EmpMapper, Emp>
└── controller/
    ├── EmpController.java    ← @RestController
    └── DeptController.java
src/main/resources/
└── mapper/
    ├── EmpMapper.xml
    └── DeptMapper.xml

MyBatis-Plus Generator 配置详解

完整 AutoGenerator 配置(非 Fast 模式)

package com.example.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
 import com.baomidou.mybatisplus.generator.config.*;
 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
 import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
 public class MpFullGenerator {
    public static void main(String[] args) {
        // ====== 1. 数据源配置 ======
        DataSourceConfig dataSourceConfig = DataSourceConfig.builder(
                "jdbc:mysql://localhost:3306/mybatis_demo?
     useSSL=false&serverTimezone=Asia/Shanghai",
                "com.mysql.cj.jdbc.Driver",
                "root",
                "123456")
            .build();
        // ====== 2. 全局配置 ======
        GlobalConfig globalConfig = GlobalConfig.builder()
                .author("example")
                .outputDir("src/main/java")
                .enableSwagger()                    // Swagger 注解
                .disableOpenDir()                    // 生成后不自动打开目录
                .commentDate("yyyy-MM-dd")
                .dateType(DateType.TIME_PACK)        // 使用 java.time 包
                .build();
        // ====== 3. 包配置 ======
        PackageConfig packageConfig = PackageConfig.builder()
                .parent("com.example")
                .moduleName("system")               // 模块名(会加到包路径中)
                .entity("entity")
                .mapper("mapper")
                .service("service")
                .serviceImpl("service.impl")
                .controller("controller")
                .xml("mapper.xml")
                .pathInfo(java.util.Collections.singletonMap(
                    OutputFile.xml,
                    "src/main/resources/mapper"))
                .build();
        // ====== 4. 模板配置 ======
        TemplateConfig templateConfig = TemplateConfig.builder()
                .entity("templates/entity.java")    // 使用自定义模板
                .mapper("templates/mapper.java")
                .xml("templates/mapper.xml")
                .service("templates/service.java")
                .serviceImpl("templates/serviceImpl.java")
                .controller("templates/controller.java")
                .build();
        // ====== 5. 策略配置 ======
         StrategyConfig strategyConfig = StrategyConfig.builder()
                 .addInclude("emp", "dept")          // 要生成的表
                 .addTablePrefix("t_", "sys_")       // 过滤表前缀
                 .addTableSuffix("_tab")             // 过滤表后缀
                 // 实体策略
                 .entityBuilder()
                 .naming(NamingStrategy.underline_to_camel)
                 .columnNaming(NamingStrategy.underline_to_camel)
                 .enableLombok()                     // Lombok 注解
                 .enableTableFieldAnnotation()       // @TableField 注解
                 .enableActiveRecord()               // ActiveRecord 模式
                 .enableFileOverride()               // 覆盖已有文件
                 .logicDeleteColumnName("deleted")   // 逻辑删除
                 .versionColumnName("version")       // 乐观锁
                 .idType(IdType.AUTO)                // 主键策略
                 .formatFileName("%s")               // 实体类名格式
                 .superClass("com.example.BaseEntity")  // 父类
                 // Mapper 策略
                 .mapperBuilder()
                 .enableMapperAnnotation()           // @Mapper 注解
                 .enableBaseResultMap()              // 生成 BaseResultMap
                 .enableBaseColumnList()             // 生成 BaseColumnList
                 .formatMapperFileName("%sMapper")
                 .formatXmlFileName("%sMapper")
                 // Service 策略
                 .serviceBuilder()
                 .formatServiceFileName("%sService")
                 .formatServiceImplFileName("%sServiceImpl")
                 .superServiceClass("com.example.BaseService")
                 .superServiceImplClass("com.example.BaseServiceImpl")
                 // Controller 策略
                 .controllerBuilder()
                 .enableRestStyle()                  // @RestController
                 .enableHyphenStyle()                // URL 连字符风格
                 .formatFileName("%sController")
                 .build();
         // ====== 6. 注入配置(自定义变量) ======
         InjectionConfig injectionConfig = InjectionConfig.builder()
                 .customMap(java.util.Map.of(
                     "author", "example",
                     "version", "1.0.0"))
                 .build();
        // ====== 7. 组装执行 ======
        AutoGenerator generator = new AutoGenerator(dataSourceConfig);
        generator.global(globalConfig);
        generator.packageInfo(packageConfig);
        generator.template(templateConfig);
        generator.strategy(strategyConfig);
        generator.injection(injectionConfig);
        generator.templateEngine(new FreemarkerTemplateEngine());
        generator.execute();
        System.out.println("✅ 代码生成完成!");
    }
}

IdType 主键策略

IdType说明常见场景
AUTO使用数据库自增主键MySQL AUTO_INCREMENT
NONE不指定局部策略,按全局配置处理由项目统一配置
INPUT插入前由开发者手动赋值业务主键
ASSIGN_ID使用默认标识符生成器分配长整型 ID分布式系统常用
ASSIGN_UUID分配不带连字符的 UUID 字符串字符串主键

NamingStrategy 命名策略

说明示例
underline_to_camel下划线命名转驼峰命名emp_name 转为 empName
no_change保持数据库名称不变emp_name 保持为 emp_name

数据源类型映射配置

 // MySQL 8.x 类型映射自定义
 DataSourceConfig dataSourceConfig = DataSourceConfig.builder(
         url, driver, username, password)
     .typeConvert(new ITypeConvert() {
         @Override
         public IColumnType executeTypeConvert(TableInfo tableInfo, String fieldName,
     String fieldType) {
             // 自定义类型映射
             if ("tinyint(1)".equalsIgnoreCase(fieldType)) {
                 return DbColumnType.BOOLEAN;
            }
            if ("json".equalsIgnoreCase(fieldType)) {
                return DbColumnType.OBJECT;
            }
            // 默认使用 MP 内置映射
            return TypeConverts.useDefault(tableInfo, fieldName, fieldType);
        }
    })
    .build();

模板自定义

自定义实体类模板

MP 使用 Freemarker 模板引擎,默认模板位于 mybatis-plus-generator jar 包中的 templates/ 目录。

自定义 entity.java.ftl 模板:

 package ${package.Entity};
 <#list table.importPackages as pkg>
 import ${pkg};
 </#list>
 <#if swagger2>
 import io.swagger.v3.oas.annotations.media.Schema;
 </#if>
 <#if entityLombokAnnotations>
import lombok.Data;
import lombok.experimental.Accessors;
</#if>
/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokAnnotations>
@Data
@Accessors(chain = true)
</#if>
<#if swagger2>
@Schema(description = "${table.comment!}")
</#if>
@TableName("${schemaName}${table.name}")
public class ${entity} implements Serializable {
    private static final long serialVersionUID = 1L;
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#if field.keyIdentityFlag>
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
        <#elseif idType??>
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
        <#else>
    @TableId(value = "${field.annotationColumnName}", type = IdType.ASSIGN_ID)
        </#if>
    <#elseif field.fill??>
    <#-- 通用填充字段 -->
    @TableField(value = "${field.annotationColumnName}", fill =
     FieldFill.${field.fill})
    <#elseif !field.propertyType?ends_with("Serializable")>
    @TableField("${field.annotationColumnName}")
    </#if>
    <#if swagger2>
    @Schema(description = "${field.comment!}")
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
<#if !entityLombokAnnotations>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>
    public ${field.propertyType} ${getprefix}${field.capitalPropertyName}() {
        return ${field.propertyName};
    }
    public void set${field.capitalPropertyName}(${field.propertyType}
     ${field.propertyName}) {
        this.${field.propertyName} = ${field.propertyName};
    }
    </#list>
</#if>
}

使用自定义模板

TemplateConfig templateConfig = TemplateConfig.builder()
    .entity("/templates/my-entity.java")    // 自定义模板路径
    .mapper("/templates/my-mapper.java")
    .xml("/templates/my-mapper.xml")
    .service("/templates/my-service.java")
    .serviceImpl("/templates/my-serviceImpl.java")
    .controller("/templates/my-controller.java")
    .build();

将自定义模板放在 src/main/resources/templates/ 目录下。

自定义 Controller 模板(RESTful 风格)

 package ${package.Controller};
 import ${package.Entity}.${entity};
 import ${package.Service}.${entity}Service;
 <#if swagger2>
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 </#if>
 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if swagger2>
@Tag(name = "${table.comment!}")
</#if>
@RestController
@RequestMapping("/${table.entityPath}")
public class ${entity}Controller {
    @Autowired
    private ${entity}Service ${table.entityPath}Service;
    @Operation(summary = "查询所有")
    @GetMapping
    public List<${entity}> list() {
        return ${table.entityPath}Service.list();
    }
    @Operation(summary = "根据ID查询")
    @GetMapping("/{id}")
    public ${entity} getById(@PathVariable ${entity} ${table.entityPath}) {
        return ${table.entityPath}Service.getById(${table.entityPath});
    }
    @Operation(summary = "新增")
    @PostMapping
    public boolean save(@RequestBody ${entity} ${table.entityPath}) {
        return ${table.entityPath}Service.save(${table.entityPath});
    }
    @Operation(summary = "修改")
    @PutMapping
    public boolean update(@RequestBody ${entity} ${table.entityPath}) {
        return ${table.entityPath}Service.updateById(${table.entityPath});
    }
    @Operation(summary = "根据ID删除")
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable ${entity} ${table.entityPath}) {
        return ${table.entityPath}Service.removeById(${table.entityPath});
    }
}

Spring Boot 集成逆向工程

Spring Boot 项目中集成 MBG

pom.xml 配置:

 <dependencies>
     <!-- Spring Boot Starter -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <!-- MyBatis Spring Boot Starter -->
     <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
    <!-- MySQL -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- MBG 插件 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.4.2</version>
            <executions>
                <!-- 可选:绑定到 generate-sources 阶段自动执行 -->
                <execution>
                    <id>Generate MyBatis Artifacts</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <!-- 取消注释则每次构建自动生成 -->
                    <!-- <phase>generate-sources</phase> -->
                </execution>
            </executions>
            <configuration>
                <configurationFile>
                    src/main/resources/generator/generatorConfig.xml
                </configurationFile>
                <overwrite>true</overwrite>
                <verbose>true</verbose>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>com.mysql</groupId>
                    <artifactId>mysql-connector-j</artifactId>
                    <version>8.0.33</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

Spring Boot + MyBatis-Plus 集成

pom.xml:

 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

application.yml:

 spring:
   datasource:
     url: jdbc:mysql://localhost:3306/mybatis_demo?
     useSSL=false&serverTimezone=Asia/Shanghai
     username: root
     password: 123456
     driver-class-name: com.mysql.cj.jdbc.Driver
 mybatis-plus:
   mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

生成的实体类使用示例

 // ====== Emp.java(MP 生成的实体类)======
 @Schema(description = "员工表")
 @TableName("emp")
 @Data
 @Accessors(chain = true)
 public class Emp implements Serializable {
     private static final long serialVersionUID = 1L;
    @Schema(description = "员工ID")
    @TableId(value = "emp_id", type = IdType.AUTO)
    private Integer empId;
    @Schema(description = "员工姓名")
    @TableField("emp_name")
    private String empName;
    @Schema(description = "性别 M-男 F-女")
    private String gender;
    @Schema(description = "邮箱")
    private String email;
    @Schema(description = "工资")
    private BigDecimal salary;
    @Schema(description = "所属部门ID")
    @TableField("dept_id")
    private Integer deptId;
    @Schema(description = "入职日期")
    private LocalDate hireDate;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
// ====== EmpMapper.java ======
@Mapper
public interface EmpMapper extends BaseMapper<Emp> {
    // BaseMapper 已提供 insert/delete/update/select 等方法
    // 可以在这里添加自定义 SQL
}
// ====== EmpService.java ======
public interface EmpService extends IService<Emp> {
    // IService 已提供通用业务方法
    // 可以在这里添加自定义业务方法
}
// ====== EmpServiceImpl.java ======
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
    // ServiceImpl 已实现 IService 的所有方法
}
 // ====== EmpController.java ======
 @Tag(name = "员工管理")
 @RestController
 @RequestMapping("/emp")
 public class EmpController {
     @Autowired
     private EmpService empService;
    @Operation(summary = "分页查询")
    @GetMapping("/page")
    public Page<Emp> page(
            @RequestParam(defaultValue = "1") Integer current,
            @RequestParam(defaultValue = "10") Integer size) {
        return empService.page(new Page<>(current, size));
    }
    @Operation(summary = "新增员工")
    @PostMapping
    public boolean save(@RequestBody Emp emp) {
        return empService.save(emp);
    }
    @Operation(summary = "根据ID查询")
    @GetMapping("/{id}")
    public Emp getById(@PathVariable Integer id) {
        return empService.getById(id);
    }
    @Operation(summary = "更新员工")
    @PutMapping
    public boolean update(@RequestBody Emp emp) {
        return empService.updateById(emp);
    }
    @Operation(summary = "删除员工")
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return empService.removeById(id);
    }
}

多模块项目中的逆向工程

多模块项目结构

my-project/
├── pom.xml                          ← 父 POM
├── my-project-common/               ← 通用模块
│   └── pom.xml
├── my-project-entity/               ← 实体模块
 │   ├── pom.xml
 │   └── src/main/java/com/example/entity/
 ├── my-project-mapper/               ← Mapper 模块
 │   ├── pom.xml
│   └── src/main/java/com/example/mapper/
├── my-project-service/              ← Service 模块
│   ├── pom.xml
│   └── src/main/java/com/example/service/
├── my-project-web/                  ← Web 模块(启动模块)
│   ├── pom.xml
│   └── src/main/resources/
│       ├── generatorConfig.xml
│       └── application.yml
└── my-project-generator/            ← 代码生成器模块(独立)
    ├── pom.xml
    └── src/main/java/com/example/Generator.java

多模块 MBG 配置

 <!-- generatorConfig.xml 放在 my-project-generator 模块中 -->
 <generatorConfiguration>
     <context id="multiModule" targetRuntime="MyBatis3">
         <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
         <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
         <commentGenerator>
             <property name="suppressAllComments" value="false"/>
        </commentGenerator>
        <jdbcConnection driverClass="${jdbc.driver}"
                        connectionURL="${jdbc.url}"
                        userId="${jdbc.username}"
                        password="${jdbc.password}">
            <property name="useInformationSchema" value="true"/>
        </jdbcConnection>
        <javaTypeResolver>
            <property name="useJSR310Types" value="true"/>
        </javaTypeResolver>
        <!-- 实体类输出到 entity 模块 -->
        <javaModelGenerator
            targetPackage="com.example.entity"
            targetProject="../my-project-entity/src/main/java">
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- XML 输出到 web 模块的 resources -->
        <sqlMapGenerator
            targetPackage="mapper"
            targetProject="../my-project-web/src/main/resources">
        </sqlMapGenerator>
        <!-- Mapper 接口输出到 mapper 模块 -->
        <javaClientGenerator
            type="XMLMAPPER"
            targetPackage="com.example.mapper"
            targetProject="../my-project-mapper/src/main/java">
        </javaClientGenerator>
        <table tableName="emp" domainObjectName="Emp">
            <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
        </table>
    </context>
</generatorConfiguration>

关键: targetProject 使用相对路径 ../模块名/src/main/java 指向其他模块的源码目录。

多模块 MP Generator 配置

 FastAutoGenerator.create(url, username, password)
     .globalConfig(builder -> builder
         .author("example")
         .outputDir(System.getProperty("user.dir") + "/my-project-generator/src/main/java")
     )
     .packageConfig(builder -> builder
         .parent("com.example")
         // 实体类输出到 entity 模块
         .entity("entity")
        // Mapper 输出到 mapper 模块
        .mapper("mapper")
        .pathInfo(java.util.Map.of(
            OutputFile.entity, "../my-project-entity/src/main/java",
            OutputFile.mapper, "../my-project-mapper/src/main/java",
            OutputFile.service, "../my-project-service/src/main/java",
            OutputFile.serviceImpl, "../my-project-service/src/main/java",
            OutputFile.controller, "../my-project-web/src/main/java",
            OutputFile.xml, "../my-project-web/src/main/resources/mapper"
        ))
    )
    .strategyConfig(builder -> builder
        .addInclude("emp", "dept")
        .entityBuilder().enableLombok()
    )
    .execute();

实战案例一:MBG 完整项目

项目搭建

# 1. 创建 Maven 项目
mvn archetype:generate -DgroupId=com.example -DartifactId=mbg-demo -DarchetypeArtifactId=maven-archetype-quickstart
# 2. 进入项目目录
cd mbg-demo
# 3. 添加依赖(参考 III 章节 pom.xml)

完整配置文件

generatorConfig.xml:

 <?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="conditional">
        <!-- 插件 -->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
        <!-- 重命名 Example 为 Query -->
        <plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
            <property name="searchString" value="Example$"/>
            <property name="replaceString" value="Query"/>
        </plugin>
        <!-- 注释生成器 -->
        <commentGenerator>
            <property name="suppressAllComments" value="false"/>
            <property name="suppressDate" value="true"/>
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>
        <!-- 数据库连接 -->
        <jdbcConnection driverClass="${jdbc.driver}"
                        connectionURL="${jdbc.url}"
                        userId="${jdbc.username}"
                        password="${jdbc.password}">
            <property name="useInformationSchema" value="true"/>
        </jdbcConnection>
        <!-- 类型解析 -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
            <property name="useJSR310Types" value="true"/>
        </javaTypeResolver>
        <!-- 实体类 -->
        <javaModelGenerator
            targetPackage="com.example.entity"
            targetProject="src/main/java">
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- XML -->
        <sqlMapGenerator
            targetPackage="mapper"
            targetProject="src/main/resources">
        </sqlMapGenerator>
        <!-- Mapper -->
        <javaClientGenerator
            type="XMLMAPPER"
            targetPackage="com.example.mapper"
            targetProject="src/main/java">
        </javaClientGenerator>
        <!-- 表配置 -->
        <table tableName="emp" domainObjectName="Emp"
               enableCountByExample="true"
               enableDeleteByExample="true"
               enableSelectByExample="true"
               enableUpdateByExample="true">
            <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
        </table>
        <table tableName="dept" domainObjectName="Dept">
            <generatedKey column="dept_id" sqlStatement="MySql" identity="true"/>
        </table>
    </context>
</generatorConfiguration>

执行生成

# 方式一:Maven 命令
mvn mybatis-generator:generate
# 方式二:运行 Java 类
# 直接运行 Generator.java 的 main 方法

编写测试

 package com.example.test;
 import com.example.entity.Emp;
 import com.example.entity.EmpQuery;
 import com.example.mapper.EmpMapper;
 import org.apache.ibatis.io.Resources;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.List;
public class EmpMapperTest {
    private SqlSession session;
    private EmpMapper empMapper;
    @Before
    public void setUp() throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        session = factory.openSession();
        empMapper = session.getMapper(EmpMapper.class);
    }
    @After
    public void tearDown() {
        if (session != null) {
            session.close();
        }
    }
    // 测试1:插入
    @Test
    public void testInsert() {
        Emp emp = new Emp();
        emp.setEmpName("赵六");
        emp.setGender("M");
        emp.setEmail("zhaoliu@qq.com");
        emp.setSalary(new BigDecimal("13000"));
        emp.setDeptId(1);
        int rows = empMapper.insert(emp);
        System.out.println("插入行数: " + rows);
        System.out.println("主键回填: " + emp.getEmpId());
         session.commit();
     }
     // 测试2:按主键查询
     @Test
     public void testSelectByPrimaryKey() {
         Emp emp = empMapper.selectByPrimaryKey(1);
         System.out.println(emp);
     }
     // 测试3:条件查询
     @Test
     public void testSelectByExample() {
         EmpQuery query = new EmpQuery();
         query.createCriteria()
                 .andGenderEqualTo("M")
                 .andSalaryGreaterThan(new BigDecimal("10000"));
         query.setOrderByClause("salary DESC");
         List<Emp> list = empMapper.selectByExample(query);
         list.forEach(System.out::println);
     }
     // 测试4:选择性更新
     @Test
     public void testUpdateByPrimaryKeySelective() {
         Emp emp = new Emp();
         emp.setEmpId(1);
         emp.setSalary(new BigDecimal("20000"));
         int rows = empMapper.updateByPrimaryKeySelective(emp);
         System.out.println("更新行数: " + rows);
         session.commit();
     }
     // 测试5:条件删除
     @Test
     public void testDeleteByExample() {
         EmpQuery query = new EmpQuery();
         query.createCriteria().andDeptIdIsNull();
         int rows = empMapper.deleteByExample(query);
         System.out.println("删除行数: " + rows);
         session.commit();
     }
     // 测试6:条件计数
     @Test
     public void testCountByExample() {
        EmpQuery query = new EmpQuery();
        query.createCriteria().andGenderEqualTo("M");
        long count = empMapper.countByExample(query);
        System.out.println("男性员工数量: " + count);
    }
}

实战案例二:MyBatis-Plus Generator 完整项目

项目搭建

 <!-- pom.xml 关键依赖 -->
 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.5.0</version>
    </dependency>
</dependencies>

生成器代码

 package com.example.generator;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.generator.FastAutoGenerator;
 import com.baomidou.mybatisplus.generator.config.OutputFile;
 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
 import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
 import java.util.HashMap;
import java.util.Map;
public class CodeGenerator {
    public static void main(String[] args) {
        // 数据库配置
        String url = "jdbc:mysql://localhost:3306/mybatis_demo" +
                     "?
     useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        // 输出目录
        String projectPath = System.getProperty("user.dir");
        FastAutoGenerator.create(url, username, password)
            .globalConfig(builder -> builder
                .author("example")
                .outputDir(projectPath + "/src/main/java")
                .enableSwagger()
                .commentDate("yyyy-MM-dd")
                .disableOpenDir()
            )
            .packageConfig(builder -> {
                Map<OutputFile, String> pathInfo = new HashMap<>();
                pathInfo.put(OutputFile.xml, projectPath +
     "/src/main/resources/mapper");
                builder
                    .parent("com.example")
                    .entity("entity")
                    .mapper("mapper")
                    .service("service")
                    .serviceImpl("service.impl")
                    .controller("controller")
                    .pathInfo(pathInfo);
            })
            .strategyConfig(builder -> builder
                .addInclude("emp", "dept")       // 要生成的表
                .addTablePrefix("t_", "sys_")    // 去掉表前缀
                .entityBuilder()
                    .naming(NamingStrategy.underline_to_camel)
                    .columnNaming(NamingStrategy.underline_to_camel)
                    .enableLombok()
                    .enableTableFieldAnnotation()
                    .idType(IdType.AUTO)
                    .logicDeleteColumnName("deleted")
                    .versionColumnName("version")
                    .formatFileName("%s")
                .mapperBuilder()
                    .enableMapperAnnotation()
                    .enableBaseResultMap()
                    .enableBaseColumnList()
                    .formatMapperFileName("%sMapper")
                    .formatXmlFileName("%sMapper")
                .serviceBuilder()
                    .formatServiceFileName("%sService")
                    .formatServiceImplFileName("%sServiceImpl")
                .controllerBuilder()
                    .enableRestStyle()
                    .enableHyphenStyle()
                    .formatFileName("%sController")
            )
            .templateEngine(new FreemarkerTemplateEngine())
            .execute();
        System.out.println("✅ 代码生成完成!");
    }
}

自动填充处理器

 package com.example.config;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 import org.apache.ibatis.reflection.MetaObject;
 import org.springframework.stereotype.Component;
 import java.time.LocalDateTime;
 /**
 * 自动填充处理器
 * 配合实体类 @TableField(fill = FieldFill.INSERT) 使用
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 新增时自动填充
        this.strictInsertFill(metaObject, "createTime",
            LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime",
            LocalDateTime.class, LocalDateTime.now());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时自动填充
        this.strictUpdateFill(metaObject, "updateTime",
            LocalDateTime.class, LocalDateTime.now());
    }
}

分页配置

 package com.example.config;
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 @Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(
            new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

测试使用

 package com.example.controller;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.example.entity.Emp;
 import com.example.service.EmpService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "员工管理")
@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;
    @Operation(summary = "分页查询")
    @GetMapping("/page")
    public Page<Emp> page(
            @RequestParam(defaultValue = "1") Integer current,
            @RequestParam(defaultValue = "10") Integer size) {
        return empService.page(new Page<>(current, size));
    }
    @Operation(summary = "条件分页查询")
    @GetMapping("/search")
    public Page<Emp> search(
            @RequestParam(defaultValue = "1") Integer current,
            @RequestParam(defaultValue = "10") Integer size,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String gender,
            @RequestParam(required = false) Integer deptId) {
        Page<Emp> page = new Page<>(current, size);
        return empService.lambdaQuery()
                .like(name != null && !name.isEmpty(), Emp::getEmpName, name)
                .eq(gender != null && !gender.isEmpty(), Emp::getGender, gender)
                .eq(deptId != null, Emp::getDeptId, deptId)
                .orderByDesc(Emp::getSalary)
                .page(page);
    }
    @Operation(summary = "新增")
    @PostMapping
    public boolean save(@RequestBody Emp emp) {
        return empService.save(emp);
    }
    @Operation(summary = "批量新增")
    @PostMapping("/batch")
    public boolean saveBatch(@RequestBody List<Emp> list) {
        return empService.saveBatch(list);
    }
    @Operation(summary = "根据ID查询")
    @GetMapping("/{id}")
    public Emp getById(@PathVariable Integer id) {
        return empService.getById(id);
    }
    @Operation(summary = "更新")
    @PutMapping
    public boolean update(@RequestBody Emp emp) {
        return empService.updateById(emp);
    }
    @Operation(summary = "根据ID删除")
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return empService.removeById(id);
    }
}

实战案例三:自定义插件生成 Swagger 注解

需求

让 MBG 生成的实体类自动添加 Swagger/OpenAPI 注解:

@Schema(description = "员工表")
public class Emp {
    @Schema(description = "员工ID")
    private Integer empId;
    @Schema(description = "员工姓名")
    private String empName;
}

自定义 Swagger 插件

 package com.example.generator;
 import org.mybatis.generator.api.IntrospectedColumn;
 import org.mybatis.generator.api.IntrospectedTable;
 import org.mybatis.generator.api.PluginAdapter;
 import org.mybatis.generator.api.dom.java.*;
 import java.util.List;
/**
 * 自定义 Swagger 注解插件
 * 自动给实体类和字段添加 @Schema 注解
 */
public class SwaggerAnnotationPlugin extends PluginAdapter {
    private static final String SCHEMA = "io.swagger.v3.oas.annotations.media.Schema";
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
    /**
     * 给实体类添加 @Schema 注解
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
                                                  IntrospectedTable introspectedTable)
     {
        // 导入 @Schema
        topLevelClass.addImportedType(SCHEMA);
        // 生成注解:@Schema(description = "表注释")
        String remark = introspectedTable.getRemarks();
        String description = (remark != null && !remark.trim().isEmpty())
                ? remark
                : introspectedTable.getFullyQualifiedTable().toString();
        topLevelClass.addAnnotation("@Schema(description = \"" + escape(description) +
     "\")");
        return true;
    }
    /**
     * 给字段添加 @Schema 注解
     */
    @Override
    public boolean modelFieldGenerated(Field field,
                                        TopLevelClass topLevelClass,
                                        IntrospectedColumn introspectedColumn,
                                        IntrospectedTable introspectedTable,
                                        ModelClassType modelClassType) {
        // 确保 @Schema 已导入
        if (!topLevelClass.getImportedTypes().stream()
                .anyMatch(t -> t.getFullyQualifiedName().equals(SCHEMA))) {
            topLevelClass.addImportedType(SCHEMA);
        }
        // 生成字段注解
        String remark = introspectedColumn.getRemarks();
        String description = (remark != null && !remark.trim().isEmpty())
                ? remark
                : introspectedColumn.getActualColumnName();
        field.addAnnotation("@Schema(description = \"" + escape(description) + "\")");
        return true;
    }
    /**
     * 转义特殊字符
     */
    private String escape(String s) {
        return s.replace("\"", "\\\"").replace("\n", " ").trim();
    }
}

配置使用

 <context id="mysqlContext" targetRuntime="MyBatis3">
     <!-- ★ Swagger 注解插件 -->
     <plugin type="com.example.generator.SwaggerAnnotationPlugin"/>
     <!-- ★ Lombok 插件(配合使用) -->
     <plugin type="com.example.generator.LombokPlugin"/>
     <!-- Serializable -->
    <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
    <commentGenerator>
        <property name="suppressAllComments" value="true"/>
    </commentGenerator>
    <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                    connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
                    userId="root" password="123456">
        <property name="useInformationSchema" value="true"/>
    </jdbcConnection>
    <javaTypeResolver>
        <property name="useJSR310Types" value="true"/>
    </javaTypeResolver>
    <javaModelGenerator targetPackage="com.example.entity"
                        targetProject="src/main/java">
        <property name="trimStrings" value="true"/>
    </javaModelGenerator>
    <sqlMapGenerator targetPackage="mapper"
                     targetProject="src/main/resources">
    </sqlMapGenerator>
    <javaClientGenerator type="XMLMAPPER"
                         targetPackage="com.example.mapper"
                         targetProject="src/main/java">
    </javaClientGenerator>
    <table tableName="emp" domainObjectName="Emp">
        <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
    </table>
</context>

生成的实体类效果

 package com.example.entity;
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "员工表")
public class Emp implements Serializable {
    private static final long serialVersionUID = 1L;
    @Schema(description = "员工ID")
    private Integer empId;
    @Schema(description = "员工姓名")
    private String empName;
    @Schema(description = "性别 M-男 F-女")
    private String gender;
    @Schema(description = "邮箱")
    private String email;
    @Schema(description = "工资")
    private BigDecimal salary;
    @Schema(description = "所属部门ID")
    private Integer deptId;
    @Schema(description = "入职日期")
    private LocalDate hireDate;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

常见问题与排错

生成后没有表注释和字段注释

原因: MySQL 8.x 默认不返回注释信息。

解决: 在 <jdbcConnection> 中添加 <property name=“useInformationSchema” value=“true”/>。

<jdbcConnection ...>
    <property name="useInformationSchema" value="true"/>
</jdbcConnection>

日期类型生成的是 Date 而不是 LocalDateTime

原因: 默认使用 java.util.Date。

解决: 在 <javaTypeResolver> 中开启 JSR-310:

<javaTypeResolver>
    <property name="useJSR310Types" value="true"/>
</javaTypeResolver>

注意:此属性在 MBG 1.4.0+ 才支持。DATETIME → LocalDateTime,DATE → LocalDate,TIME → LocalTime。

文件覆盖问题:XML 文件没有完全覆盖

原因: MBG 默认对 XML 文件使用 merge(合并) 策略,而不是完全覆盖。这是为了保护你手写的自定义 SQL。

解决: 使用 UnmergeableXmlMapperPlugin 插件强制覆盖:

1<plugin type=“org.mybatis.generator.plugins.UnmergeableXmlMapperPlugin”/>

生成的包名多了一层 schema 名

原因: enableSubPackages 设为 true 时,会将 schema 名作为包的后缀。

解决: 设置 enableSubPackages=“false”:

<javaModelGenerator targetPackage="com.example.entity" ...>
    <property name="enableSubPackages" value="false"/>
</javaModelGenerator>

主键没有回填(insert 后实体类中 id 为 null)

原因: 没有配置 <generatedKey>。

解决:

<table tableName="emp" domainObjectName="Emp">
    <generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
</table>

Maven 插件运行时找不到驱动

原因: Maven 插件的 classpath 中没有 MySQL 驱动。

解决: 在 <plugin> 的 <dependencies> 中添加驱动:

 <plugin>
     <groupId>org.mybatis.generator</groupId>
     <artifactId>mybatis-generator-maven-plugin</artifactId>
     <version>1.4.2</version>
     <dependencies>
         <dependency>
             <groupId>com.mysql</groupId>
             <artifactId>mysql-connector-j</artifactId>
             <version>8.0.33</version>
        </dependency>
    </dependencies>
</plugin>

MySQL 关键字冲突(如desc, order, key

原因: 表名或字段名是 MySQL 保留字。

解决: 在 context 上配置分隔符:

<context id="mysqlContext" targetRuntime="MyBatis3"
         autoDelimitKeywords="true"
         beginningDelimiter="`"
         endingDelimiter="`">

或者在 table 上配置:

1<table tableName=“order” delimitIdentifiers=“true”>

MP Generator 生成的代码在错误的目录

原因: outputDir 路径不正确。

解决: 使用 System.getProperty(“user.dir”) 确保路径正确:

String projectPath = System.getProperty("user.dir");
FastAutoGenerator.create(url, username, password)
    .globalConfig(builder -> builder
        .outputDir(projectPath + "/src/main/java")  // 绝对路径
    )
    ...

MBG 生成文件中文乱码

原因: 编码不一致。

解决: 在 pom.xml 中配置编码:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

并在 Maven 插件中配置:

<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <configuration>
        <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
    </configuration>
</plugin>

MP Generator 3.5.x API 变化

问题: 从 3.4.x 升级到 3.5.x 后 API 不兼容。

主要变化:

3.4.x(旧)3.5.x(新)
new AutoGenerator()new AutoGenerator(dataSourceConfig)
generator.setDataSource(config)构造器参数传入
generator.setGlobalConfig(config)generator.global(config)
generator.setStrategy(config)generator.strategy(config)
generator.setPackageInfo(config)generator.packageInfo(config)
generator.setTemplate(config)generator.template(config)

最佳实践

生成策略选择

是否需要 Service / Controller?
├── 是 → 使用 MyBatis-Plus Generator
│        (自带 Service / Controller / BaseMapper)

└── 否 → 使用 MyBatis Generator
         (仅生成 Entity / Mapper / XML / Example)

将生成代码与自定义代码分离

核心原则:生成的代码可以随时重新生成,不要直接修改。

正确的做法:
├── 生成的 Mapper 接口 ← 保持不变(继承 BaseMapper 扩展)
├── 自定义 SQL ← 新建 CustomXxxMapper.java 或在同一 XML 中追加
└── 业务逻辑 ← 写在 Service 层
错误的做法:
├── 直接在生成的 EmpMapper.java 中添加方法 ← 重新生成会被覆盖
├── 直接在生成的 EmpMapper.xml 中修改 SQL ← merge 可能保留,但有风险
└── 直接在生成的 Emp.java 中添加逻辑 ← 重新生成会被覆盖

XML 自定义 SQL 扩展方案

 <!-- EmpMapper.xml — MBG 生成的部分保持不变 -->
 <!-- ====== 以下为手动追加的自定义 SQL ====== -->
 <!-- 多表关联查询 -->
 <select id="selectEmpWithDept" resultMap="EmpWithDeptResultMap">
     SELECT e.*, d.dept_name, d.location
     FROM emp e
     LEFT JOIN dept d ON e.dept_id = d.dept_id
    WHERE e.emp_id = #{empId}
</select>
<resultMap id="EmpWithDeptResultMap" type="com.example.dto.EmpDeptDTO">
    <id column="emp_id" property="empId"/>
    <result column="emp_name" property="empName"/>
    <result column="gender" property="gender"/>
    <result column="salary" property="salary"/>
    <result column="dept_name" property="deptName"/>
    <result column="location" property="location"/>
</resultMap>

配置管理建议

项目结构建议:
├── src/main/resources/
│   ├── generator/
│   │   ├── generatorConfig.xml      ← MBG 配置
│   │   ├── generator.properties     ← 数据库连接属性
│   │   └── generator-dev.properties ← 开发环境属性(可选)
│   ├── mapper/
│   │   └── EmpMapper.xml            ← 生成的 XML
│   └── mybatis-config.xml

生成 vs 手写决策表

场景建议方式
单表 CRUD逆向工程生成
单表条件查询MBG 的 Example 或 MP 的 LambdaQueryWrapper
多表 JOIN 查询手写 SQL(XML 或注解)
复杂统计报表手写 SQL
分页查询MP 分页插件
批量操作MP 的 saveBatch / updateBatchById

速查表

MBG 配置元素速查表

元素作用必填
<generatorConfiguration>元素
<properties>引入属性文件
<classPathEntry>数据库驱动路径Maven 方式可省
<context>生成上下文
<plugin>插件配置
<commentGenerator>注释生成器
<jdbcConnection>数据库连接
<javaTypeResolver>类型解析器
<javaModelGenerator>实体类生成器
<sqlMapGenerator>XML 生成器XMLMAPPER 模式必填
<javaClientGenerator>Mapper 接口生成器
<table>表配置

MBG 内置插件速查表

插件功能
SerializablePlugin实体类实现 Serializable
ToStringPlugin生成 toString()
EqualsAndHashCodePlugin生成 equals() 和 hashCode()
FluentBuilderMethodsPlugin链式 setter
RowBoundsPluginRowBounds 分页
RenameExampleClassPlugin重命名 Example 类
MapperNamePlugin自定义 Mapper 名称
UnmergeableXmlMapperPluginXML 强制覆盖
CaseInsensitiveLikePlugin大小写不敏感 LIKE
VirtualKeyPlugin虚拟主键

MBG targetRuntime 速查表

生成 Example生成 XML说明
MyBatis3完整版(默认)
MyBatis3Simple简化版(仅基本 CRUD)
MyBatis3DynamicSql动态 SQL DSL 版
MyBatis3KotlinKotlin 版

MP Generator 配置速查表

配置项方法说明
作者globalConfig.author(“xxx”)注释中的作者名
输出目录globalConfig.outputDir(“path”)代码输出根目录
SwaggerglobalConfig.enableSwagger()开启 Swagger 注解
父包packageConfig.parent(“com.xxx”)根包名
表名strategyConfig.addInclude(“table”)指定要生成的表
表前缀strategyConfig.addTablePrefix(“t_“)去掉表前缀
LombokentityBuilder.enableLombok()使用 Lombok
主键策略entityBuilder.idType(IdType.AUTO)主键生成策略
命名策略entityBuilder.naming(…)下划线转驼峰
RestControllercontrollerBuilder.enableRestStyle()REST 风格

Example 方法速查表

 EmpExample example = new EmpExample();
 // 创建 AND 条件组
 EmpExample.Criteria criteria = example.createCriteria();
 // 等值
 criteria.andXxxEqualTo(value);          // = value
 criteria.andXxxNotEqualTo(value);       // <> value
// 范围
criteria.andXxxGreaterThan(value);      // > value
criteria.andXxxGreaterThanOrEqualTo(value); // >= value
criteria.andXxxLessThan(value);         // < value
criteria.andXxxLessThanOrEqualTo(value); // <= value
criteria.andXxxBetween(v1, v2);        // BETWEEN v1 AND v2
criteria.andXxxNotBetween(v1, v2);     // NOT BETWEEN
// 集合
criteria.andXxxIn(list);               // IN (...)
criteria.andXxxNotIn(list);            // NOT IN (...)
// 模糊
criteria.andXxxLike("%keyword%");      // LIKE '%keyword%'
criteria.andXxxNotLike("%keyword%");   // NOT LIKE '%keyword%'
// NULL
criteria.andXxxIsNull();               // IS NULL
criteria.andXxxIsNotNull();            // IS NOT NULL
// OR 条件组
example.or().andXxxEqualTo(value);     // OR (...)
// 排序
example.setOrderByClause("salary DESC");
// 去重
example.setDistinct(true);

类型映射速查表

MySQL 类型Java 类型(useJSR310Types=trueJDBC 类型
INTIntegerINTEGER
BIGINTLongBIGINT
TINYINTIntegerTINYINT
SMALLINTIntegerSMALLINT
DECIMALBigDecimalDECIMAL
VARCHARStringVARCHAR
CHARStringCHAR
TEXTStringLONGVARCHAR
DATELocalDateDATE
DATETIMELocalDateTimeTIMESTAMP
TIMELocalTimeTIME
TIMESTAMPLocalDateTimeTIMESTAMP
BLOBbyte[]LONGVARBINARY
BITBooleanBIT
FLOATFloatFLOAT
DOUBLEDoubleDOUBLE
JSONStringVARCHAR

常用命令速查表

 # ====== MBG ======
 # Maven 插件执行
 mvn mybatis-generator:generate
 # 指定配置文件
 mvn mybatis-generator:generate -DconfigurationFile=src/main/resources/generatorConfig.xml
 # 不覆盖已有文件
mvn mybatis-generator:generate -Doverwrite=false
# 命令行执行
java -jar mybatis-generator-core-1.4.2.jar -configfile generatorConfig.xml -overwrite
# ====== MP Generator ======
# 直接运行 main 方法(IDE 中右键运行)
# 或使用 Maven exec 插件
mvn exec:java -Dexec.mainClass="com.example.generator.CodeGenerator"

总结: MyBatis 逆向工程的核心在于理解「数据库表结构 → Java 代码」的自动映射过程。MyBatis Generator 是官方工具,稳定可靠,适合生成基础 DAO 层代码;MyBatis-Plus Generator 功能更丰富,能一键生成从 Entity 到 Controller 的全栈代码。两者都可以通过自定义插件和模板深度定制生成结果。掌握逆向工程可以大幅提升开发效率,但需谨记:应将可重复生成的代码与自定义代码分离,并在重新生成前确认覆盖范围;扩展查询可放在独立 Mapper、扩展接口或自定义 SQL 中

喜欢的话,留下你的评论吧~

... 访问量
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka