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 handwriting | Reverse engineering |
|---|---|
| Manual entity class creation, field by field mapping | Automatic generation, one-click completion |
| Handwritten CRUD SQL statement | automatically generates single table CRUD |
| is prone to misspelling and missing fields | is completely consistent with the database |
| The new fields in | need to be manually modified. |
| takes time and boring | seconds to complete |
Comparison of mainstream reverse engineering tools
| Tools | Full name | Maintainer | Features |
|---|---|---|---|
| MyBatis Generator (MBG) | MyBatis Generator | MyBatisOfficial | Official**Tool*, stable and reliable, generate entities +Mapper+XML+ Example |
| MyBatis-Plus Generator | MP CodeGenerator | MyBatis-Plus Community | has stronger functions, generates entities +Mapper+XML+Service+Controller, supports template |
| IDEA plug-in (Free MyBatis Tool) | EasyCode, MyBatisCodeHelper | Third-party | visual operation, suitable for rapid development of |
| RuoYi Code Generator | If you followcode generation | RuoYi Community | comes 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
-
Read the
generatorConfig.xmlor Java configuration. -
Connect to the database through JDBC and read metadata such as tables, fields, primary keys, and comments.
-
Convert JDBC types to Java types.
-
Generate entity classes, Mapper interfaces, XML mapping files, and optional Example condition classes based on the run mode and template.
-
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.jarMaven 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
Method 1: Running Java code (flexible scenarios recommended)
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.
Method 2: Run the Maven plug-in (recommended regular projects)
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
| Features | JavaCode Running | MavenPlug-in | Command Line |
|---|---|---|---|
| Flexibility | Maximum (Programmable Control) | Medium | Minimum |
| Configuration Complexity | Medium | Low | Low |
| suitable for scenarios | requires custom logic | Daily development | CI/CD scripts |
| IDE supports | to run | directly on the Maven panel. Click | and 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="`">
| Properties | Description | Common Values |
|---|---|---|
id | Unique identification of the current context | Any non-repeating string |
targetRuntime | Select generator running mode | MyBatis3, MyBatis3Simple, MyBatis3DynamicSql, etc. |
defaultModelType | Setting model structure | conditional, flat, hierarchical |
autoDelimitKeywords | Does it automatically add separators to database keywords | true, false |
beginningDelimiter | Keyword start separator | MySQL usually uses back quotes |
endingDelimiter | Keyword ending Separator | MySQL usually uses back quotes |
Detailed explanation of targetRuntime
| Value | Description | product |
|---|---|---|
| MyBatis3 | Default, generate complete code | Entity + Example + Mapper + XML (including Example method) |
| MyBatis 3Simple | simplified version, does not generate Example | entity + Mapper + XML (basic CRUD only) |
| MyBatis 3DynamicSql | Dynamic SQL Edition (requires MyBatis 3.4.0+) | Entity + Mapper (uses DSL, no XML) |
| MyBatis3 Kotlin | Kotlin Edition | Kotlin 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
| Value | Description | Use Scenarios |
|---|---|---|
| conditional | default. 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 generated | Most scenarios |
| flat | All tables generate a flat entity class, and the primary key is also used as a common field | Simple single table |
| hierarchical | Generation hierarchy: primary key class inheritance → basic entity class → classes with BLOBs | Complex 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&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
| value | description | product |
|---|---|---|
XMLMAPPER | interface is separated from XML mapping file, suitable for most projects | Mapper interface and corresponding XML |
ANNOTATEDMAPPER | SQL is mainly written in the interface through annotations | Mapper interface, no independent XML |
MIXEDMAPPER | Basic statements use annotations, and complex statements retain the XML | Mapper 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>| Properties | Description |
|---|---|
column | Primary key column name |
sqlStatement | A statement or database ID to obtain the primary key value, for example, MySql |
identity | true means the database’s self-added primary key; false means the primary key value obtained from a statement |
type | pre 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.xmlFunctional 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
| Requirements | Example Method | SQL Operator |
|---|---|---|
| equals | andXxxEqualTo(value) | = |
| is not equal to | andXxxNotEqualTo(value) | <> |
| is greater than | andXxxGreaterThan(value) | > |
| is greater than or equal to | andXxxGreaterThanOrEqualTo(value) | >= |
| is less than | andXxxLessThan(value) | < |
| is less than or equal to | andXxxLessThanOrEqualTo(value) | <= |
| Fuzzy matching | andXxxLike(value) | LIKE |
| Non-fuzzy matching | andXxxNotLike(value) | NOT LIKE |
| The | set contains | andXxxIn(list) |
| collection does not contain | andXxxNotIn(list) | NOT IN |
| Interval matching | andXxxBetween(v1, v2) | BETWEEN |
| is empty | andXxxIsNull() | IS NULL |
| Non-empty | andXxxIsNotNull() | IS NOT NULL |
| Add OR Condition Group | example.or().andXxx...() | OR |
| Sort | example.setOrderByClause("xxx DESC") | ORDER BY |
| Deweighting | example.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 Class | Functions |
|---|---|
| SerializablePlugin | allows entity classes to implement Serializeable interfaces |
| ToStringPlugin | Generating toString() Method |
| EqualsAndHashCodePlugin | Generating equals() and hashCode() methods |
| RowBoundsPlugin | Generating paging method (based on RowBoundsand not recommended for MySQL) |
| VirtualKeyPlugin | Virtual Primary Key Plugin |
| FluentBuilderMethodsPlugin | Generating chain setter (returns to this) |
| MapperNamePlugin | Custom Mapper Name |
| RenameExampleClassPlugin | Renaming Example Class |
| CaseInsitiveLikePlugin | Case Insensitive LIKE Query |
| UnmergeableXmlMapperPlugin | XML 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.xmlDetailed 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 Value | Description | Common Scenarios |
|---|---|---|
AUTO | Use the database self-added primary key | MySQL AUTO_INCREMENT etc. |
NONE | Local policies are not specified and processed according to global configuration | Unified configuration by project |
INPUT | Assign manually by developer before insertion | Business PK |
ASSIGN_ID | Assign long IDs using the default identifier generator | Commonly used distributed systems |
ASSIGN_UUID | Assign a UUID string without hyphen | String primary key |
Naming Strategy
| Value | Description | Example |
|---|---|---|
underline_to_camel | Underline naming to hump naming | emp_name to empName |
no_change | Keep database name unchanged | emp_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: 0Example 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.javaMulti-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.xmlGenerate vs handwritten decision table
| Scenario | Suggestion Method |
|---|---|
| single table CRUD | reverse engineering to generate |
| Single table conditional query | MBG’s Example or MP’s LambdaQueryWrapper |
| Multi-table JOIN query | Handwritten SQL (XML or annotation) |
| Complex Statistical Report | Handwritten SQL |
| paging query | MP paging plug-in |
| Batch Operation | MP’s saveBatch /updateBatchById |
cheat sheet
MBG configuration element quick lookup table
| Element | Function | Required |
|---|---|---|
| <generatorConfiguration> | root element | is |
| <properties> | Introducing attribute file | No |
| <classPathEntry> | Database driving path | Maven method can save |
| <context> | Generation context | is |
| <plugin> | Plug-in Configuration | No |
| <commentGenerator> | Comment Generator | No |
| <jdbcConnection> | database connection | is |
| <javaTypeResolver> | Type Parser | No |
| <javaModelGenerator> | Entity Class Generator | is |
| <sqlMapGenerator> | XML Generator | XMLMAPPER Mode Required |
| <javaClientGenerator> | Mapper interface generator | is |
| <table> | Table Configuration | is |
MBG built-in plug-in quick lookup table
| Plug-in | Features |
|---|---|
| SerializablePlugin | Entity Class Implementation Serializable |
| ToStringPlugin | Generate toString() |
| EqualsAndHashCodePlugin | Generating equals() and hashCode() |
| FluentBuilderMethodsPlugin | Chain setter |
| RowBounds Plugin | RowBounds paging |
| RenameExampleClassPlugin | Renaming Example Class |
| MapperNamePlugin | Custom Mapper Name |
| UnmergeableXmlMapperPlugin | XML mandatory override of |
| CaseInsitiveLikePlugin | case-insensitive LIKE |
| VirtualKeyPlugin | Virtual Primary Key |
MBG targetRuntime quick lookup table
| Value | GenerationExample | GenerationXML | Description |
|---|---|---|---|
| MyBatis3 | Full version (default) | ||
| MyBatis 3Simple | Simplified version (Basic CRUD only) | ||
| MyBatis 3DynamicSql | Dynamic SQL DSL Edition | ||
| MyBatis3 Kotlin | Kotlin Edition |
MP Generator Configuration Quick Checklist
| Configuration Items | Method | Description |
|---|---|---|
| author | globalConfig.author(“xxx”) | author name in the annotation |
| output directory | globalConfig.outputDir(“path”) | code output root directory |
| Swagger | globalConfig.enableSwagger() | Open Swagger annotation |
| parent package | packageConfig.parent(“com.xxx”) | root package name |
| Table Name | strategyConfig.addInclude(“table”) | Specifies the table to be generated |
| Table prefix | strategyConfig.addTablePrefix(“t_“) | Remove table prefix |
| Lombok | entityBuilder.enableLombok() | Use Lombok |
| Primary Key Policy | entityBuilder.idType(IdType.AUTO) | Primary Key Generation Policy |
| naming strategy | entityBuilder.naming(…) | Underline to hump |
| RestController | controllerBuilder.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 type | Java type (useJSR310Types=true) | JDBC type |
|---|---|---|
INT | Integer | INTEGER |
BIGINT | Long | BIGINT |
TINYINT | Integer | TINYINT |
SMALLINT | Integer | SMALLINT |
DECIMAL | BigDecimal | DECIMAL |
VARCHAR | String | VARCHAR |
CHAR | String | CHAR |
TEXT | String | LONGVARCHAR |
DATE | LocalDate | DATE |
DATETIME | LocalDateTime | TIMESTAMP |
TIME | LocalTime | TIME |
TIMESTAMP | LocalDateTime | TIMESTAMP |
BLOB | byte[] | LONGVARBINARY |
BIT | Boolean | BIT |
FLOAT | Float | FLOAT |
DOUBLE | Double | DOUBLE |
JSON | String | VARCHAR |
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~