MyBatis Complete Guide to Reverse Engineering

Published 2026-07-30 19:30 Updated 2026-07-30 19:30 11469 words 58 min read ... Page views

This article provides a comprehensive introduction to the core concepts and practices of MyBatis reverse engineering, covering the two major tools: MyBatis Generator (MBG) and MyBatis-Plus code generator. MBG automatically generates entity classes, Mapper interfaces and XML mapping files based on the database table structure through configuration files or code methods, and supports custom annotations, plug-ins and templates;MyBatis-Plus Generator is more powerful and can generate full-stack code such as entities, Mapper, Services, and Controllers with one click, and supports Lombok, Swagger annotations and custom templates. The article explains in detail the key configuration points, troubleshooting common problems and best practices, emphasizing that generated code should be separated from custom logic to ensure maintainability and repeatability.

MyBatis Complete Guide to Reverse Engineering

This document comprehensively covers all knowledge points of MyBatis reverse engineering (code generator), including the official MyBatis Generator (MBG) tool and MyBatis-Plus code generator, from configuration to actual combat, nothing is left out.

Overview of reverse engineering

What is reverse engineering

MyBatis Reverse Engineering refers to the process of automatically generating Java entity classes, Mapper interfaces, Mapper XML mapping files ** through database table structures.

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

Why reverse engineering is needed

Traditional handwritingReverse engineering
Manual entity class creation, field by field mappingAutomatic generation, one-click completion
Handwritten CRUD SQL statementautomatically generates single table CRUD
is prone to misspelling and missing fieldsis completely consistent with the database
The new fields inneed to be manually modified.
takes time and boringseconds to complete

Comparison of mainstream reverse engineering tools

ToolsFull nameMaintainerFeatures
MyBatis Generator (MBG)MyBatis GeneratorMyBatisOfficialOfficial**Tool*, stable and reliable, generate entities +Mapper+XML+ Example
MyBatis-Plus GeneratorMP CodeGeneratorMyBatis-Plus Communityhas stronger functions, generates entities +Mapper+XML+Service+Controller, supports template
IDEA plug-in (Free MyBatis Tool)EasyCode, MyBatisCodeHelperThird-partyvisual operation, suitable for rapid development of
RuoYi Code GeneratorIf you followcode generationRuoYi Communitycomes with a template engine to generate complete front and rear codes

What can reverse engineering produce?

MyBatis Generator product:

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 product:

 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 Core Concepts

Overview of MBG Architecture

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

MBG Workflow

  1. Read the generatorConfig.xml or Java configuration.

  2. Connect to the database through JDBC and read metadata such as tables, fields, primary keys, and comments.

  3. Convert JDBC types to Java types.

  4. Generate entity classes, Mapper interfaces, XML mapping files, and optional Example condition classes based on the run mode and template.

  5. Writes the build results to the specified directory. You should confirm overwrite and merge strategies before rebuilding, paying particular attention to the merge behavior of XML files.

MBG Release Notes

MyBatis Generator uses the 1.4.x version series. The examples in this article use 1.4.2, and the version number is used to ensure that the examples are reproducible and does not represent the “latest version” at any time.

Use org.mybatis.generator:mybatis-generator-core:1.4.2 for Maven coordinates.

Environmental preparation and dependence

project directory structure

 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 Dependency (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>

sample database

 -- 创建数据库
 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());

Three operating modes of MBG

 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("✅ 代码生成完成!");
    }
}

Operation method: Directly click on the main method to run.

After the plug-in has been configured in pom.xml, execute the command:

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

Note: Maven plug-in method reads src/main/resources/generatorConfig.xml by default. If the configuration file is placed at this path, no additional specification is required.

Method 3: Run on the command line (suitable for scripting)

# 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 输出详细信息

Three ways to compare

FeaturesJavaCode RunningMavenPlug-inCommand Line
FlexibilityMaximum (Programmable Control)MediumMinimum
Configuration ComplexityMediumLowLow
suitable for scenariosrequires custom logicDaily developmentCI/CD scripts
IDE supportsto rundirectly on the Maven panel. Clickand you need to manually prepare jar

Detailed explanation of generatorConfig.xml configuration

Complete configuration template

 <?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>

External properties file (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

Detailed explanation of context elements

the context attribute

<context> is the core of the configuration, and all generation rules are defined in it.

<context id="mysqlContext"
         targetRuntime="MyBatis3"
         defaultModelType="conditional"
         introspectedColumnImpl="org.mybatis.generator.internal.db.DatabaseIntrospector"
         autoDelimitKeywords="false"
         beginningDelimiter="`"
         endingDelimiter="`">
PropertiesDescriptionCommon Values
idUnique identification of the current contextAny non-repeating string
targetRuntimeSelect generator running modeMyBatis3, MyBatis3Simple, MyBatis3DynamicSql, etc.
defaultModelTypeSetting model structureconditional, flat, hierarchical
autoDelimitKeywordsDoes it automatically add separators to database keywordstrue, false
beginningDelimiterKeyword start separatorMySQL usually uses back quotes
endingDelimiterKeyword ending SeparatorMySQL usually uses back quotes

Detailed explanation of targetRuntime

ValueDescriptionproduct
MyBatis3Default, generate complete codeEntity + Example + Mapper + XML (including Example method)
MyBatis 3Simplesimplified version, does not generate Exampleentity + Mapper + XML (basic CRUD only)
MyBatis 3DynamicSqlDynamic SQL Edition (requires MyBatis 3.4.0+)Entity + Mapper (uses DSL, no XML)
MyBatis3 KotlinKotlin EditionKotlin Entity + DSL Mapper

Mapper method generated by MyBatis3Simple (without Example):

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

Mapper method generated by MyBatis3 (with Example):

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

Detailed explanation of defaultModelType

ValueDescriptionUse Scenarios
conditionaldefault. If the table has only one primary key, no primary key class is generated; if there is a compound primary key, a primary key class is generatedMost scenarios
flatAll tables generate a flat entity class, and the primary key is also used as a common fieldSimple single table
hierarchicalGeneration hierarchy: primary key class inheritance → basic entity class → classes with BLOBsComplex table with BLOB field

Class structure generated by hierarchical pattern:

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

jdbcConnection -Database connection

basic configuration

<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 special configuration

 <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>

Important: MySQL 8.x does not return table comments and field comments by default. useInformationSchema=true must be set, otherwise the addRemarkComments for the commentGenerator are invalid.

Reference for connection configuration of each database

 <!-- 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 -Entity Class Generation

basic configuration

 <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>

Example of generated entity class

Configuration: 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 example

// 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;
     }
}

After configuring rootClass, the create_time and update_time fields will not be generated repeatedly in subclasses.

sqlMapGenerator - XML mapping file generation

basic configuration

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

Generated XML example (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 interface generation

basic configuration

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

type attributes

valuedescriptionproduct
XMLMAPPERinterface is separated from XML mapping file, suitable for most projectsMapper interface and corresponding XML
ANNOTATEDMAPPERSQL is mainly written in the interface through annotationsMapper interface, no independent XML
MIXEDMAPPERBasic statements use annotations, and complex statements retain the XMLMapper interface and some XML

Generated Mapper interface example (XMLMAPPER pattern)

 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);
}

Interface generated by ANNOTATEDMAPPER pattern

// 纯注解,没有 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);
    // ... 其他方法
}

Detailed explanation of table element

table complete attribute

 <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 wildcard usage

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

generatedKey -Backfilling of primary key

 <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>
PropertiesDescription
columnPrimary key column name
sqlStatementA statement or database ID to obtain the primary key value, for example, MySql
identitytrue means the database’s self-added primary key; false means the primary key value obtained from a statement
typepre means to obtain the primary key before insertion, and post means to obtain the primary key after insertion

columnOverride -Column override

<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 -Ignore columns

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

domainObjectReningRule-Entity Class Naming Rule

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

columnReningRule-Column renaming rule

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

Description of the generated code structure

complete directory structure

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

Functional comparison table of each method

MethodFunction corresponds toSQL

INSERT INTO emp (…) VALUES insert(Emp record) Insert all fields (…)

insertSelective(Emp record)

Selective insertion (null word INSERT INTO emp (…) VALUES segment not inserted)(…) +

DELETE FROM emp WHERE emp_id deleteByPrimaryKey(Integer id) Delete by primary key= ?

deleteByExample(EmpExample example) Delete DELETE FROM emp WHERE by condition…

UPDATE emp SET … WHERE updateByPrimaryKey(Emp record) Full update emp_id = ?

updateByPrimaryKeySelective(EmpUPDATE emp SET … WHERE selective update record)emp_id = ? +

updateByExample(…) Update UPDATE emp SET in full according to conditions… WHERE …

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

updateByExampleSelective(…) Selective updates based on conditions

SELECT … FROM emp WHERE selectByPrimaryKey(Integer id) Query by primary key emp_id = ?

 SELECT ... FROM emp WHERE
...

selectByExample(EmpExample example) Query by condition

SELECT COUNT(*) FROM emp countByExample(EmpExample example) Count by Condition WHERE…

Detailed explanation of the Example class (QBC style query)

The role of the Example class

The Example class is a query condition constructorautomatically generated by MBG. It adopts theQBC (Query By Criteria) style and builds WHERE conditions through chain calls to avoid handwritten SQL.

Example class structure

 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 Common query examples

 // ====== 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 Usage table

RequirementsExample MethodSQL Operator
equalsandXxxEqualTo(value)=
is not equal toandXxxNotEqualTo(value)<>
is greater thanandXxxGreaterThan(value)>
is greater than or equal toandXxxGreaterThanOrEqualTo(value)>=
is less thanandXxxLessThan(value)<
is less than or equal toandXxxLessThanOrEqualTo(value)<=
Fuzzy matchingandXxxLike(value)LIKE
Non-fuzzy matchingandXxxNotLike(value)NOT LIKE
Theset containsandXxxIn(list)
collection does not containandXxxNotIn(list)NOT IN
Interval matchingandXxxBetween(v1, v2)BETWEEN
is emptyandXxxIsNull()IS NULL
Non-emptyandXxxIsNotNull()IS NOT NULL
Add OR Condition Groupexample.or().andXxx...()OR
Sortexample.setOrderByClause("xxx DESC")ORDER BY
Deweightingexample.setDistinct(true)DISTINCT

Customize CommentGenerator

Problems with default annotations

The default comment format generated by MBG:

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

Problems: Comments are useless, no database field comments, no date format.

Customize 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 注释(保持简洁)
    }
}

Using custom comment generators in configuration files

 <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>

Generated entity class effects

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

Customize Plugin Plug-ins

Overview of Plugin Mechanism

MBG extends the code generation process through the Plugin plug-in mechanism. Plug-ins can intercept the generation process and modify or add generated code.

 MBG 生成流程

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

List of built-in plug-ins

Plug-in ClassFunctions
SerializablePluginallows entity classes to implement Serializeable interfaces
ToStringPluginGenerating toString() Method
EqualsAndHashCodePluginGenerating equals() and hashCode() methods
RowBoundsPluginGenerating paging method (based on RowBoundsand not recommended for MySQL)
VirtualKeyPluginVirtual Primary Key Plugin
FluentBuilderMethodsPluginGenerating chain setter (returns to this)
MapperNamePluginCustom Mapper Name
RenameExampleClassPluginRenaming Example Class
CaseInsitiveLikePluginCase Insensitive LIKE Query
UnmergeableXmlMapperPluginXML does not merge and directly overwrites

Custom Plugin Example: Lombok Plug-in

Let the generated entity class use Lombok annotations instead of the lengthy 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
    }
}

Use custom plug-ins in configuration

 <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>

Custom Plugin example: Mapper inherits the common interface

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 Code Generator (3.5.x+)

MP Generator Overview

The MyBatis-Plus code generator is more powerful than MBG and can generate:

  • Entity entity class (supports Lombok / Swagger annotation)
  • Mapper interface (inherits BaseMapper)
  • Mapper XML mapping file
  • Service Interface+ Service Implementation Class
  • Controller (with RESTful interface)

dependent configuration

 <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>

Fast AutoGenerator

MyBatis-Plus 3.5.3+ provides FastAutoGenerator, and the chain configuration is completed in one line:

 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 代码生成完成!");
    }
}

Generated directory structure

 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

Detailed explanation of MyBatis-Plus Generator configuration

Full AutoGenerator configuration (non-Fast mode)

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 Primary Key Policy

IdType ValueDescriptionCommon Scenarios
AUTOUse the database self-added primary keyMySQL AUTO_INCREMENT etc.
NONELocal policies are not specified and processed according to global configurationUnified configuration by project
INPUTAssign manually by developer before insertionBusiness PK
ASSIGN_IDAssign long IDs using the default identifier generatorCommonly used distributed systems
ASSIGN_UUIDAssign a UUID string without hyphenString primary key

Naming Strategy

ValueDescriptionExample
underline_to_camelUnderline naming to hump namingemp_name to empName
no_changeKeep database name unchangedemp_name Keep emp_name

Data source type mapping configuration

 // 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();

Template customization

Custom entity class template

MP uses the Freemarker template engine, and the default template is located in the templates/directory in the mybatis-plus-generator jar package.

Customize entity.java.ftl template:

 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>
}

Use custom templates

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();

Place the custom template in the src/main/resources/templates/directory.

Custom Controller template (RESTful style)

 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 integrates reverse engineering

Integrate MBG into Spring Boot projects

pom.xml configuration:

 <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 integration

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

Example of usage of generated entity classes

 // ====== 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);
    }
}

Reverse engineering in multi-module projects

Multi-module project structure

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

Multi-module MBG configuration

 <!-- 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>

Key: targetProject uses relative paths../ The module name/src/main/java points to the source catalog of other modules.

Multi-module MP Generator configuration

 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();

Practical Case 1: MBG Complete Project

Project construction

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

full profile

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>

to perform generating

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

writing tests

 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);
    }
}

Practical Case 2: MyBatis-Plus Generator Complete Project

Project construction

 <!-- 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>

generator code

 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("✅ 代码生成完成!");
    }
}

autofill processor

 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());
    }
}

paging configuration

 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;
    }
}

test uses

 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);
    }
}

Practical case 3: Custom plug-in generates Swagger annotations

demand

Let the entity classes generated by MBG automatically add Swagger/OpenAPI annotations:

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

Customize Swagger plug-ins

 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();
    }
}

configuration uses

 <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>

Generated entity class effects

 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;
}

Frequently Asked Questions and Troubleshooting

No table comments and field comments after generation

Reason: MySQL 8.x does not return comment information by default.

Solution: Add<property name=“useInformationSchema” value=“true”/> to<jdbcConnection>.

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

Date type generates Date instead of LocalDateTime

Reason: Default uses java.util.Date.

Solution: Open JSR-310 in<javaTypeResolver>:

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

Note: This attribute is only supported in MBG 1.4.0+. DATETIME → LocalDateTime,DATE → LocalDate,TIME → LocalTime。

File coverage problem: XML files are not completely overwritten

Reason:MBG uses themerge policy on XML files by default, rather than completely overwriting them. This is to protect your handwritten custom SQL.

Solution: Use the UnmergeableXmlMapperPlugin plug-in to force override:

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

The generated package name has an extra layer of schema name

Reason: When enableSubPackages is set to true, the schema name is used as the suffix of the package.

Solution: Set enableSubPackages=“false”:

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

The primary key is not backfilled (id in the entity class after insert is null)

Reason:<generatedKey> is not configured.

Resolution:

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

Maven plug-in cannot find drivers while running

Reason: There is no MySQL driver in the classpath of the Maven plug-in.

Solution: Add drivers in<dependencies> in<plugin>:

 <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 keyword conflicts (such as desc, order, key)

Reason: Table name or field name is a MySQL reserved word.

Solution: Configure separators on context:

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

Or configure it on the table:

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

Code generated by MP Generator is in the wrong directory

Reason: The outputDir path is incorrect.

Solution: Use System.getProperty(“user.dir”) to ensure the correct path:

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

MBG generates file Chinese garbled code

Reason: Inconsistent coding.

Solution: Configure encoding in pom. xml:

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

And configure it in the Maven plug-in:

<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 changes

Problem: The APIs are incompatible after upgrading from 3.4.x to 3.5.x.

Main changes:

3.4.x (old)3.5.x (new)
new AutoGenerator()new AutoGenerator(dataSourceConfig)
generator.setDataSource(config)constructor parameters are passed to
generator.setGlobalConfig(config)generator.global(config)
generator.setStrategy(config)generator.strategy(config)
generator.setPackageInfo(config)generator.packageInfo(config)
generator.setTemplate(config)generator.template(config)

best practices

Generation strategy selection

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

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

Separate generated code from custom code

Core principle: The generated code can be regenerated at any time and not directly modified.

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

XML Custom SQL Extension Scheme

 <!-- 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>

Configuration management recommendations

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

Generate vs handwritten decision table

ScenarioSuggestion Method
single table CRUDreverse engineering to generate
Single table conditional queryMBG’s Example or MP’s LambdaQueryWrapper
Multi-table JOIN queryHandwritten SQL (XML or annotation)
Complex Statistical ReportHandwritten SQL
paging queryMP paging plug-in
Batch OperationMP’s saveBatch /updateBatchById

cheat sheet

MBG configuration element quick lookup table

ElementFunctionRequired
<generatorConfiguration>root elementis
<properties>Introducing attribute fileNo
<classPathEntry>Database driving pathMaven method can save
<context>Generation contextis
<plugin>Plug-in ConfigurationNo
<commentGenerator>Comment GeneratorNo
<jdbcConnection>database connectionis
<javaTypeResolver>Type ParserNo
<javaModelGenerator>Entity Class Generatoris
<sqlMapGenerator>XML GeneratorXMLMAPPER Mode Required
<javaClientGenerator>Mapper interface generatoris
<table>Table Configurationis

MBG built-in plug-in quick lookup table

Plug-inFeatures
SerializablePluginEntity Class Implementation Serializable
ToStringPluginGenerate toString()
EqualsAndHashCodePluginGenerating equals() and hashCode()
FluentBuilderMethodsPluginChain setter
RowBounds PluginRowBounds paging
RenameExampleClassPluginRenaming Example Class
MapperNamePluginCustom Mapper Name
UnmergeableXmlMapperPluginXML mandatory override of
CaseInsitiveLikePlugincase-insensitive LIKE
VirtualKeyPluginVirtual Primary Key

MBG targetRuntime quick lookup table

ValueGenerationExampleGenerationXMLDescription
MyBatis3Full version (default)
MyBatis 3SimpleSimplified version (Basic CRUD only)
MyBatis 3DynamicSqlDynamic SQL DSL Edition
MyBatis3 KotlinKotlin Edition

MP Generator Configuration Quick Checklist

Configuration ItemsMethodDescription
authorglobalConfig.author(“xxx”)author name in the annotation
output directoryglobalConfig.outputDir(“path”)code output root directory
SwaggerglobalConfig.enableSwagger()Open Swagger annotation
parent packagepackageConfig.parent(“com.xxx”)root package name
Table NamestrategyConfig.addInclude(“table”)Specifies the table to be generated
Table prefixstrategyConfig.addTablePrefix(“t_“)Remove table prefix
LombokentityBuilder.enableLombok()Use Lombok
Primary Key PolicyentityBuilder.idType(IdType.AUTO)Primary Key Generation Policy
naming strategyentityBuilder.naming(…)Underline to hump
RestControllercontrollerBuilder.enableRestStyle()REST Style

Example method quick lookup table

 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);

Type mapping quick lookup table

MySQL typeJava type (useJSR310Types=true)JDBC type
INTIntegerINTEGER
BIGINTLongBIGINT
TINYINTIntegerTINYINT
SMALLINTIntegerSMALLINT
DECIMALBigDecimalDECIMAL
VARCHARStringVARCHAR
CHARStringCHAR
TEXTStringLONGVARCHAR
DATELocalDateDATE
DATETIMELocalDateTimeTIMESTAMP
TIMELocalTimeTIME
TIMESTAMPLocalDateTimeTIMESTAMP
BLOBbyte[]LONGVARBINARY
BITBooleanBIT
FLOATFloatFLOAT
DOUBLEDoubleDOUBLE
JSONStringVARCHAR

Quick lookup table of commonly used commands

 # ====== 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"

**Summary:**The core of MyBatis reverse engineering is to understand the automatic mapping process of “database table structure → Java code”. MyBatis Generator is an official tool that is stable and reliable and suitable for generating basic DAO layer code;MyBatis-Plus Generator is more functional and can generate full-stack code from Entity to Controller with one click. Both can be deeply customized to generate results through custom plug-ins and templates. Mastering reverse engineering can greatly improve development efficiency, but keep in mind:Repeatedly generated code should be separated from custom code, and coverage should be confirmed before being regenerated; extended queries can be placed in a stand-alone Mapper, extended interfaces, or customized SQL.

If you enjoyed this, leave a comment~

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