MyBatis 逆向工程完全指南
本文档全面覆盖 MyBatis 逆向工程(代码生成器)的所有知识点,包括 MyBatis Generator (MBG) 官方工具与 MyBatis-Plus 代码生成器,从配置到实战,无遗漏。
逆向工程概述
什么是逆向工程
MyBatis 逆向工程(Reverse Engineering)是指通过数据库表结构,自动生成 Java 实体类、Mapper 接口、 Mapper XML 映射文件的过程。
数据库表结构 ──→ 逆向工程工具 ──→ Java 实体类
(Generator) Mapper 接口
Mapper XML
Example 类
Service(MP)
Controller(MP)
为什么需要逆向工程
| 传统手写方式 | 逆向工程方式 |
|---|---|
| 手动创建实体类,逐个字段映射 | 自动生成,一键完成 |
| 手写 CRUD 的 SQL 语句 | 自动生成单表 CRUD |
| 容易拼写错误、遗漏字段 | 与数据库完全一致 |
| 新增字段需要手动修改多处 | 重新生成即可 |
| 耗时且枯燥 | 秒级完成 |
主流逆向工程工具对比
| 工具 | 全称 | 维护方 | 特点 |
|---|---|---|---|
| MyBatis Generator (MBG) | MyBatis Generator | MyBatis 官方 | 官方工具,稳定可靠,生成实体+Mapper+XML+Example |
| MyBatis-Plus Generator | MP Code Generator | MyBatis-Plus 社区 | 功能更强,生成实体+Mapper+XML+Service+Controller,支持模板 |
| IDEA 插件(Free MyBatis Tool) | EasyCode、MyBatisCodeHelper | 第三方 | 可视化操作,适合快速开发 |
| RuoYi 代码生成器 | 若依代码生成 | RuoYi 社区 | 自带模板引擎,生成完整前后端代码 |
逆向工程能生成什么
MyBatis Generator 生成物:
src/main/java/
└── com/example/entity/
├── User.java ← 实体类(POJO)
└── UserExample.java ← 查询条件类(QBC风格)
└── com/example/mapper/
└── UserMapper.java ← Mapper 接口
src/main/resources/
└── mapper/
└── UserMapper.xml ← SQL 映射文件MyBatis-Plus Generator 生成物:
src/main/java/
└── com/example/
├── entity/User.java ← 实体类(含 Lombok / Swagger 注解)
├── mapper/UserMapper.java ← Mapper 接口(继承 BaseMapper)
├── service/
│ ├── UserService.java ← Service 接口
│ └── impl/UserServiceImpl.java ← Service 实现
└── controller/UserController.java ← Controller
src/main/resources/
└── mapper/
└── UserMapper.xml ← SQL 映射文件(可选)MyBatis Generator 核心概念
MBG 架构总览
┌──────────────────────────────────────────────────┐
│ MyBatis Generator │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ Database │ │ Generator │ │ Generated │ │
│ │ Metadata │──→│ Engine │──→│ Code │ │
│ │ (JDBC) │ │ (Java) │ │ (Java/XML) │ │
│ └──────────┘ └─────┬─────┘ └──────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ Configuration │ │
│ │ (XML / Java) │ │
│ └─────────────────┘ │
└──────────────────────────────────────────────────┘MBG 工作流程
-
读取
generatorConfig.xml或 Java 配置。 -
通过 JDBC 连接数据库并读取表、字段、主键和注释等元数据。
-
将 JDBC 类型转换为 Java 类型。
-
根据运行模式和模板生成实体类、Mapper 接口、XML 映射文件以及可选的 Example 条件类。
-
将生成结果写入指定目录。重新生成前应确认覆盖和合并策略,尤其要注意 XML 文件的合并行为。
MBG 版本说明
MyBatis Generator 使用 1.4.x 版本系列。本文示例采用 1.4.2,版本号用于保证示例可复现,不代表任何时点的“最新版本”。
Maven 坐标使用 org.mybatis.generator:mybatis-generator-core:1.4.2。
环境准备与依赖
项目目录结构
mybatis-generator-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/
│ │ │ ├── entity/
│ │ │ ├── mapper/
│ │ │ └── generator/
│ │ │ └── Generator.java
│ │ └── resources/
│ │ ├── generatorConfig.xml
│ │ └── mapper/
│ └── test/
└── lib/
└── mysql-connector-j-8.0.33.jarMaven 依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mybatis-generator-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!-- MyBatis Generator 核心 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.2</version>
</dependency>
<!-- Log4j2(可选,用于日志输出) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.22.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- MyBatis Generator Maven 插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<configuration>
<!-- 配置文件路径 -->
<configurationFile>
src/main/resources/generatorConfig.xml
</configurationFile>
<!-- 是否覆盖已有文件 -->
<overwrite>true</overwrite>
<!-- 是否将 MBG 的输出打印到控制台 -->
<verbose>true</verbose>
</configuration>
<dependencies>
<!-- MySQL 驱动(插件也需要) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>示例数据库
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mybatis_demo DEFAULT CHARSET utf8mb4;
USE mybatis_demo;
-- 部门表
CREATE TABLE dept (
dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID',
dept_name VARCHAR(50) NOT NULL COMMENT '部门名称',
location VARCHAR(100) COMMENT '部门位置'
) COMMENT '部门表';
-- 员工表
CREATE TABLE emp (
emp_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
emp_name VARCHAR(50) NOT NULL COMMENT '员工姓名',
gender CHAR(1) DEFAULT 'M' COMMENT '性别 M-男 F-女',
email VARCHAR(100) COMMENT '邮箱',
salary DECIMAL(10,2) COMMENT '工资',
dept_id INT COMMENT '所属部门ID',
hire_date DATE COMMENT '入职日期',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT
'更新时间',
FOREIGN KEY (dept_id) REFERENCES dept(dept_id)
) COMMENT '员工表';
-- 插入测试数据
INSERT INTO dept VALUES (1, '研发部', '北京'), (2, '市场部', '上海'), (3, '财务部', '广州');
INSERT INTO emp VALUES
(1, '张三', 'M', 'zhangsan@qq.com', 15000.00, 1, '2020-01-15', NOW(), NOW()),
(2, '李四', 'F', 'lisi@qq.com', 12000.00, 2, '2021-03-20', NOW(), NOW()),
(3, '王五', 'M', 'wangwu@qq.com', 18000.00, 1, '2019-07-01', NOW(), NOW());MBG 三种运行方式
方式一:Java 代码运行(推荐灵活场景)
package com.example.generator;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Generator {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<>();
boolean overwrite = true; // 是否覆盖已有文件
// 指向配置文件
File configFile = new File("src/main/resources/generatorConfig.xml");
ConfigurationParser parser = new ConfigurationParser(warnings);
Configuration config = parser.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
// 执行生成
generator.generate(null);
// 打印警告信息
for (String warning : warnings) {
System.out.println(warning);
}
System.out.println("✅ 代码生成完成!");
}
}运行方式: 直接右键 main 方法运行。
方式二:Maven 插件运行(推荐常规项目)
pom.xml 中已配置好插件后,执行命令:
# 在项目根目录执行
mvn mybatis-generator:generate
# 如果配置文件不在默认位置,指定路径
mvn mybatis-generator:generate -DconfigurationFile=src/main/resources/generatorConfig.xml
# 如果不想覆盖已有文件
mvn mybatis-generator:generate -Doverwrite=false
注意: Maven 插件方式默认读取 src/main/resources/generatorConfig.xml,如果配置文件放在此路径,无需额外指定。
方式三:命令行运行(适合脚本化)
# 1. 准备 mybatis-generator-core 和数据库驱动
# 2. 准备 generatorConfig.xml
# 3. 执行生成命令
java -jar mybatis-generator-core-1.4.2.jar -configfile generatorConfig.xml -overwrite
# 常用参数:
# -configfile 指定配置文件路径
# -overwrite 允许覆盖可覆盖的 Java 文件
# -verbose 输出详细信息三种方式对比
| 特性 | Java 代码运行 | Maven 插件 | 命令行 |
|---|---|---|---|
| 灵活性 | 最高(可编程控制) | 中等 | 最低 |
| 配置复杂度 | 中等 | 低 | 低 |
| 适合场景 | 需要自定义逻辑 | 日常开发 | CI/CD 脚本 |
| IDE 支持 | 直接运行 | Maven 面板点击 | 需手动准备 jar |
generatorConfig.xml 配置详解
完整配置模板
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- ① 引入外部属性文件 -->
<properties resource="generator.properties"/>
<!-- ② classPathEntry:指定数据库驱动路径(Maven插件方式可省略) -->
<classPathEntry location="lib/mysql-connector-j-8.0.33.jar"/>
<!-- ③ context:生成上下文(可配置多个) -->
<context id="mysqlContext" targetRuntime="MyBatis3"
defaultModelType="conditional">
<!-- ====== 内置插件配置 ====== -->
<!-- ④ 序列化插件(让实体类实现 Serializable) -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- ⑤ ToString 插件(生成 toString 方法) -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!-- ⑥ EqualsAndHashCode 插件 -->
<plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
<!-- ⑦ Row Bounds 插件(分页用,MySQL 不推荐) -->
<!-- <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"/> -->
<!-- ⑧ 虚拟主键插件 -->
<!-- <plugin type="org.mybatis.generator.plugins.VirtualKeyPlugin"/> -->
<!-- ====== 注释生成器 ====== -->
<!-- ⑨ 自定义注释生成器 -->
<commentGenerator>
<!-- 是否去除自动生成的注释 true:去除 false:保留 -->
<property name="suppressAllComments" value="false"/>
<!-- 是否生成注释中的日期 -->
<property name="suppressDate" value="true"/>
<!-- 是否添加数据库表的字段备注 -->
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- ====== 数据库连接 ====== -->
<!-- ⑩ JDBC 连接配置 -->
<jdbcConnection
driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
<!-- MySQL 8.x 需要设置以下属性,才能获取表注释 -->
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
<!-- ====== 类型解析 ====== -->
<!-- ⑪ Java 类型解析器 -->
<javaTypeResolver>
<!-- 是否强制使用 BigDecimal -->
<property name="forceBigDecimals" value="false"/>
<!-- 是否使用 JSR-310 日期类型(java.time) -->
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
<!-- ====== 代码生成器 ====== -->
<!-- ⑫ Java 实体类生成器 -->
<javaModelGenerator
targetPackage="com.example.entity"
targetProject="src/main/java">
<!-- 是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值是否清理前后的空格 -->
<property name="trimStrings" value="true"/>
<!-- 是否为实体类生成构造方法 -->
<property name="constructorBased" value="false"/>
<!-- 是否不可变(final 类,全参构造,无 setter) -->
<property name="immutable" value="false"/>
<!-- 父类(所有实体类继承的基类) -->
<!-- <property name="rootClass" value="com.example.BaseEntity"/> -->
</javaModelGenerator>
<!-- ⑬ SQL 映射文件生成器 -->
<sqlMapGenerator
targetPackage="mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- ⑭ Mapper 接口生成器 -->
<javaClientGenerator
type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
<!-- 父接口(所有 Mapper 继承的接口) -->
<!-- <property name="rootInterface" value="com.example.BaseMapper"/> -->
</javaClientGenerator>
<!-- ====== 表配置 ====== -->
<!-- ⑮ table:每张表配置一个 -->
<table schema="mybatis_demo" tableName="emp"
domainObjectName="Emp"
enableCountByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
enableUpdateByExample="true"
enableDeleteByPrimaryKey="true"
enableInsert="true"
enableSelectByPrimaryKey="true"
enableUpdateByPrimaryKey="true">
<!-- 主键策略:将自增主键回填到实体 -->
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
<!-- 列覆盖:强制指定某列的 Java 类型 -->
<!-- <columnOverride column="salary" property="salary"
javaType="java.math.BigDecimal"/> -->
<!-- 列忽略:不生成该字段 -->
<!-- <ignoreColumn column="create_time"/> -->
</table>
<table schema="mybatis_demo" tableName="dept"
domainObjectName="Dept"
enableCountByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
enableUpdateByExample="true">
<generatedKey column="dept_id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>外部属性文件(generator.properties)
# generator.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
context 元素详解
context 属性
<context> 是配置的核心,所有生成规则都在其中定义。
<context id="mysqlContext"
targetRuntime="MyBatis3"
defaultModelType="conditional"
introspectedColumnImpl="org.mybatis.generator.internal.db.DatabaseIntrospector"
autoDelimitKeywords="false"
beginningDelimiter="`"
endingDelimiter="`">
| 属性 | 说明 | 常用值 |
|---|---|---|
id | 当前上下文的唯一标识 | 任意不重复字符串 |
targetRuntime | 选择生成器运行模式 | MyBatis3、MyBatis3Simple、MyBatis3DynamicSql 等 |
defaultModelType | 设置模型类结构 | conditional、flat、hierarchical |
autoDelimitKeywords | 是否自动为数据库关键字添加分隔符 | true、false |
beginningDelimiter | 关键字起始分隔符 | MySQL 通常使用反引号 |
endingDelimiter | 关键字结束分隔符 | MySQL 通常使用反引号 |
targetRuntime 详解
| 值 | 说明 | 生成物 |
|---|---|---|
| MyBatis3 | 默认,生成完整代码 | 实体 + Example + Mapper + XML(含 Example 方法) |
| MyBatis3Simple | 简化版,不生成 Example | 实体 + Mapper + XML(仅基本 CRUD) |
| MyBatis3DynamicSql | 动态 SQL 版(需 MyBatis 3.4.0+) | 实体 + Mapper(使用 DSL,无 XML) |
| MyBatis3Kotlin | Kotlin 版 | Kotlin 实体 + DSL Mapper |
MyBatis3Simple 生成的 Mapper 方法(无 Example):
insert()
deleteByPrimaryKey()
updateByPrimaryKey()
selectByPrimaryKey()
selectAll()
MyBatis3 生成的 Mapper 方法(有 Example):
insert()
deleteByPrimaryKey()
updateByPrimaryKey()
selectByPrimaryKey()
selectAll() (Simple 没有)
countByExample()
deleteByExample()
selectByExample()
updateByExampleSelective()
updateByExample()defaultModelType 详解
| 值 | 说明 | 使用场景 |
|---|---|---|
| conditional | 默认。如果表只有一个主键,不生成主键类;有复合主键则生成主键类 | 大多数场景 |
| flat | 所有表都生成一个扁平的实体类,主键也作为普通字段 | 简单单表 |
| hierarchical | 生成层次结构:主键类继承→基础实体类→带 BLOB 的类 | 有 BLOB 字段的复杂表 |
hierarchical 模式生成的类结构:
Emp.java ← 继承 EmpKey,包含普通字段
EmpKey.java ← 仅包含主键字段
EmpWithBLOBs.java ← 继承 Emp,包含 BLOB 字段(如 TEXT/LONGTEXT)
jdbcConnection — 数据库连接
基本配置
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_demo?
useSSL=false&serverTimezone=Asia/Shanghai"
userId="root"
password="123456">
</jdbcConnection>
MySQL 8.x 特殊配置
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
userId="root"
password="123456">
<!-- ★ 关键:使用 information_schema 获取表/字段注释 -->
<property name="useInformationSchema" value="true"/>
<!-- 其他可选属性 -->
<property name="nullCatalogMeansCurrent" value="true"/>
<property name="characterEncoding" value="utf-8"/>
<property name="serverTimezone" value="Asia/Shanghai"/>
</jdbcConnection>重要: MySQL 8.x 默认不返回表注释和字段注释。必须设置 useInformationSchema=true,否则 commentGenerator 的 addRemarkComments 无效。
各数据库连接配置参考
<!-- MySQL 8.x -->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_demo?
useSSL=false&serverTimezone=Asia/Shanghai"
userId="root" password="123456">
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
<!-- PostgreSQL -->
<jdbcConnection
driverClass="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost:5432/mydb"
userId="postgres" password="123456"/>
<!-- Oracle -->
<jdbcConnection
driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
userId="scott" password="tiger"/>
<!-- SQL Server -->
<jdbcConnection
driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
connectionURL="jdbc:sqlserver://localhost:1433;databaseName=mydb"
userId="sa" password="123456"/>javaModelGenerator — 实体类生成
基本配置
<javaModelGenerator
targetPackage="com.example.entity"
targetProject="src/main/java">
<!-- 是否让 schema 作为包名后缀 -->
<!-- true: com.example.entity.mybatis_demo.Emp -->
<!-- false: com.example.entity.Emp -->
<property name="enableSubPackages" value="false"/>
<!-- 是否清理从数据库返回的字符串前后的空格 -->
<property name="trimStrings" value="true"/>
<!-- 是否基于构造方法(生成全参构造 + setter) -->
<property name="constructorBased" value="false"/>
<!-- 是否生成不可变类(final + 全参构造 + 无 setter) -->
<property name="immutable" value="false"/>
<!-- 所有实体类的父类 -->
<!-- <property name="rootClass" value="com.example.BaseEntity"/> -->
</javaModelGenerator>生成的实体类示例
配置: targetPackage=“com.example.entity”, trimStrings=“true”
package com.example.entity;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
public class Emp {
private Integer empId;
private String empName;
private String gender;
private String email;
private BigDecimal salary;
private Integer deptId;
private LocalDate hireDate;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// ====== 构造方法 ======
public Emp(Integer empId, String empName, String gender, String email,
BigDecimal salary, Integer deptId, LocalDate hireDate,
LocalDateTime createTime, LocalDateTime updateTime) {
this.empId = empId;
this.empName = empName == null ? null : empName.trim();
this.gender = gender;
this.email = email == null ? null : email.trim();
this.salary = salary;
this.deptId = deptId;
this.hireDate = hireDate;
this.createTime = createTime;
this.updateTime = updateTime;
}
public Emp() {
}
// ====== getter / setter ======
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName == null ? null : empName.trim();
}
// ... 其他 getter/setter 省略 ...
}rootClass 示例
// BaseEntity.java — 所有实体类的父类
package com.example.entity;
import java.time.LocalDateTime;
public abstract class BaseEntity {
private LocalDateTime createTime;
private LocalDateTime updateTime;
// getter / setter
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime;
}
public LocalDateTime getUpdateTime() { return updateTime; }
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime;
}
}配置 rootClass 后,create_time 和 update_time 字段不会被重复生成在子类中。
sqlMapGenerator — XML 映射文件生成
基本配置
<sqlMapGenerator
targetPackage="mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
生成的 XML 示例(EmpMapper.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.EmpMapper">
<resultMap id="BaseResultMap" type="com.example.entity.Emp">
<id column="emp_id" property="empId" jdbcType="INTEGER"/>
<result column="emp_name" property="empName" jdbcType="VARCHAR"/>
<result column="gender" property="gender" jdbcType="CHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="salary" property="salary" jdbcType="DECIMAL"/>
<result column="dept_id" property="deptId" jdbcType="INTEGER"/>
<result column="hire_date" property="hireDate" jdbcType="DATE"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 通用列名 -->
<sql id="Base_Column_List">
emp_id, emp_name, gender, email, salary, dept_id, hire_date, create_time,
update_time
</sql>
<!-- 按主键查询 -->
<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM emp
WHERE emp_id = #{empId,jdbcType=INTEGER}
</select>
<!-- 删除 -->
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
DELETE FROM emp
WHERE emp_id = #{empId,jdbcType=INTEGER}
</delete>
<!-- 插入 -->
<insert id="insert" parameterType="com.example.entity.Emp"
useGeneratedKeys="true" keyProperty="empId">
INSERT INTO emp (emp_name, gender, email,
salary, dept_id, hire_date,
create_time, update_time)
VALUES (#{empName,jdbcType=VARCHAR}, #{gender,jdbcType=CHAR},
#{email,jdbcType=VARCHAR}, #{salary,jdbcType=DECIMAL},
#{deptId,jdbcType=INTEGER}, #{hireDate,jdbcType=DATE},
#{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP})
</insert>
<!-- 选择性插入(null 字段不插入) -->
<insert id="insertSelective" parameterType="com.example.entity.Emp"
useGeneratedKeys="true" keyProperty="empId">
INSERT INTO emp
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="empName != null">emp_name,</if>
<if test="gender != null">gender,</if>
<if test="email != null">email,</if>
<if test="salary != null">salary,</if>
<if test="deptId != null">dept_id,</if>
<if test="hireDate != null">hire_date,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="empName != null">#{empName,jdbcType=VARCHAR},</if>
<if test="gender != null">#{gender,jdbcType=CHAR},</if>
<if test="email != null">#{email,jdbcType=VARCHAR},</if>
<if test="salary != null">#{salary,jdbcType=DECIMAL},</if>
<if test="deptId != null">#{deptId,jdbcType=INTEGER},</if>
<if test="hireDate != null">#{hireDate,jdbcType=DATE},</if>
<if test="createTime != null">#{createTime,jdbcType=TIMESTAMP},</if>
<if test="updateTime != null">#{updateTime,jdbcType=TIMESTAMP},</if>
</trim>
</insert>
<!-- 选择性更新 -->
<update id="updateByPrimaryKeySelective" parameterType="com.example.entity.Emp">
UPDATE emp
<set>
<if test="empName != null">emp_name = #{empName,jdbcType=VARCHAR},</if>
<if test="gender != null">gender = #{gender,jdbcType=CHAR},</if>
<if test="email != null">email = #{email,jdbcType=VARCHAR},</if>
<if test="salary != null">salary = #{salary,jdbcType=DECIMAL},</if>
<if test="deptId != null">dept_id = #{deptId,jdbcType=INTEGER},</if>
<if test="hireDate != null">hire_date = #{hireDate,jdbcType=DATE},</if>
<if test="createTime != null">create_time = #
{createTime,jdbcType=TIMESTAMP},</if>
<if test="updateTime != null">update_time = #
{updateTime,jdbcType=TIMESTAMP},</if>
</set>
WHERE emp_id = #{empId,jdbcType=INTEGER}
</update>
<!-- 全量更新 -->
<update id="updateByPrimaryKey" parameterType="com.example.entity.Emp">
UPDATE emp
SET emp_name = #{empName,jdbcType=VARCHAR},
gender = #{gender,jdbcType=CHAR},
email = #{email,jdbcType=VARCHAR},
salary = #{salary,jdbcType=DECIMAL},
dept_id = #{deptId,jdbcType=INTEGER},
hire_date = #{hireDate,jdbcType=DATE},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = #{updateTime,jdbcType=TIMESTAMP}
WHERE emp_id = #{empId,jdbcType=INTEGER}
</update>
</mapper>javaClientGenerator — Mapper 接口生成
基本配置
<javaClientGenerator
type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
type 属性
| 值 | 说明 | 生成物 |
|---|---|---|
XMLMAPPER | 接口与 XML 映射文件分离,适合大多数项目 | Mapper 接口与对应 XML |
ANNOTATEDMAPPER | SQL 主要通过注解写在接口中 | Mapper 接口,无独立 XML |
MIXEDMAPPER | 基础语句使用注解,复杂语句保留 XML | Mapper 接口与部分 XML |
生成的 Mapper 接口示例(XMLMAPPER 模式)
package com.example.mapper;
import com.example.entity.Emp;
import com.example.entity.EmpExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface EmpMapper {
// ====== 按 Example 查询 ======
long countByExample(EmpExample example);
int deleteByExample(EmpExample example);
List<Emp> selectByExample(EmpExample example);
int updateByExampleSelective(@Param("record") Emp record,
@Param("example") EmpExample example);
int updateByExample(@Param("record") Emp record,
@Param("example") EmpExample example);
// ====== 按主键操作 ======
int deleteByPrimaryKey(Integer empId);
int insert(Emp record);
int insertSelective(Emp record);
Emp selectByPrimaryKey(Integer empId);
int updateByPrimaryKeySelective(Emp record);
int updateByPrimaryKey(Emp record);
}ANNOTATEDMAPPER 模式生成的接口
// 纯注解,没有 XML 文件
public interface EmpMapper {
@Insert({
"INSERT INTO emp (emp_name, gender, ...)",
"VALUES (#{empName,jdbcType=VARCHAR}, #{gender,jdbcType=CHAR}, ...)"
})
@Options(useGeneratedKeys = true, keyProperty = "empId")
int insert(Emp record);
@Select({
"SELECT emp_id, emp_name, gender, ...",
"FROM emp",
"WHERE emp_id = #{empId,jdbcType=INTEGER}"
})
@ResultMap("BaseResultMap")
Emp selectByPrimaryKey(Integer empId);
// ... 其他方法
}table 元素详解
table 完整属性
<table
schema="mybatis_demo" <!-- 数据库名/schema -->
tableName="emp" <!-- 数据库表名(支持通配符 %) -->
domainObjectName="Emp" <!-- 生成的实体类名 -->
mapperName="EmpMapper" <!-- 生成的 Mapper 接口名(可含包名) -->
sqlMapName="EmpMapper" <!-- 生成的 XML 文件名 -->
alias="e" <!-- 表别名(用于多表查询) -->
enableCountByExample="true" <!-- 是否生成 countByExample -->
enableDeleteByExample="true" <!-- 是否生成 deleteByExample -->
enableSelectByExample="true" <!-- 是否生成 selectByExample -->
enableUpdateByExample="true" <!-- 是否生成 updateByExample -->
enableDeleteByPrimaryKey="true" <!-- 是否生成 deleteByPrimaryKey -->
enableInsert="true" <!-- 是否生成 insert -->
enableSelectByPrimaryKey="true" <!-- 是否生成 selectByPrimaryKey -->
enableUpdateByPrimaryKey="true" <!-- 是否生成 updateByPrimaryKey -->
selectByPrimaryKeyQueryId="false"
selectByExampleQueryId="false"
modelType="conditional" <!-- 覆盖 context 的 defaultModelType -->
delimitIdentifiers="false" <!-- 是否给所有标识符加分隔符 -->
delimitAllColumns="false" <!-- 是否给所有列加分隔符 -->
>tableName 通配符用法
<!-- 生成所有表(慎用) -->
<table tableName="%"/>
<!-- 生成所有以 t_ 开头的表 -->
<table tableName="t_%" />
<!-- 排除某些表(需配合 table 的 schema 属性) -->
<table tableName="%" >
<!-- 通过 domainObjectRenamingRule 来调整命名 -->
</table>
generatedKey — 主键回填
<table tableName="emp" domainObjectName="Emp">
<!-- 方式一:MySQL 自增主键 -->
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
<!-- 方式二:SQL Server 自增主键 -->
<!-- <generatedKey column="id" sqlStatement="SQL Server" identity="true"/> -->
<!-- 方式三:Oracle 序列 -->
<!-- <generatedKey column="id" sqlStatement="select SEQ_EMP.nextval from dual"
identity="false"/> -->
<!-- 方式四:PostgreSQL -->
<!-- <generatedKey column="id" sqlStatement="PostgreSQL" identity="true"/> -->
</table>| 属性 | 说明 |
|---|---|
column | 主键列名 |
sqlStatement | 获取主键值的语句或数据库标识,例如 MySQL 常用 MySql |
identity | true 表示数据库自增主键;false 表示由语句取得主键值 |
type | pre 表示插入前获取主键,post 表示插入后获取主键 |
columnOverride — 列覆盖
<table tableName="emp" domainObjectName="Emp">
<!-- 强制指定某列的 Java 类型和 jdbcType -->
<columnOverride column="salary"
property="salary"
javaType="java.math.BigDecimal"
jdbcType="DECIMAL"/>
<!-- 强制指定枚举类型 -->
<columnOverride column="gender"
property="gender"
javaType="com.example.enums.Gender"
typeHandler="com.example.handler.GenderTypeHandler"/>
<!-- 修改属性名(驼峰自定义) -->
<columnOverride column="emp_name" property="name"/>
<!-- 强制 BLOB 类型 -->
<columnOverride column="content" isBlob="true" javaType="java.lang.String"/>
</table>ignoreColumn — 忽略列
<table tableName="emp" domainObjectName="Emp">
<!-- 不生成这些字段 -->
<ignoreColumn column="create_time"/>
<ignoreColumn column="update_time"/>
<!-- 支持正则匹配 -->
<!-- <ignoreColumn pattern="^temp_.*"/> -->
</table>
domainObjectRenamingRule — 实体类重命名规则
<table tableName="t_emp" domainObjectName="Emp">
<!-- 去掉表名前缀 t_ -->
<domainObjectRenamingRule searchString="^T_" replaceString=""/>
</table>
columnRenamingRule — 列重命名规则
<table tableName="emp">
<!-- 去掉列名前缀 F_ -->
<columnRenamingRule searchString="^F_" replaceString=""/>
</table>
生成的代码结构说明
完整目录结构
mybatis-generator-demo/
├── src/main/java/com/example/
│ ├── entity/
│ │ ├── Emp.java ← 员工实体类
│ │ ├── EmpExample.java ← 员工查询条件类
│ │ ├── Dept.java ← 部门实体类
│ │ └── DeptExample.java ← 部门查询条件类
│ ├── mapper/
│ │ ├── EmpMapper.java ← 员工 Mapper 接口
│ │ └── DeptMapper.java ← 部门 Mapper 接口
│ └── generator/
│ └── Generator.java ← 生成器启动类
├── src/main/resources/
│ ├── generatorConfig.xml ← MBG 配置文件
│ ├── generator.properties ← 属性文件
│ ├── mapper/
│ │ ├── EmpMapper.xml ← 员工 SQL 映射
│ │ └── DeptMapper.xml ← 部门 SQL 映射
│ └── mybatis-config.xml ← MyBatis 主配置
└── pom.xml各方法功能对照表
方法功能对应 SQL
INSERT INTO emp (…) VALUES insert(Emp record)插入全部字段(…)
insertSelective(Emp record)
选择性插入(null 字 INSERT INTO emp (…) VALUES 段不插入)(…) +
DELETE FROM emp WHERE emp_id deleteByPrimaryKey(Integer id)按主键删除= ?
deleteByExample(EmpExample example)按条件删除 DELETE FROM emp WHERE …
UPDATE emp SET … WHERE updateByPrimaryKey(Emp record)全量更新 emp_id = ?
updateByPrimaryKeySelective(EmpUPDATE emp SET … WHERE 选择性更新 record)emp_id = ? +
updateByExample(…)按条件全量更新 UPDATE emp SET … WHERE …
UPDATE emp SET ... WHERE ...
+ <if>
updateByExampleSelective(…)按条件选择性更新
SELECT … FROM emp WHERE selectByPrimaryKey(Integer id)按主键查询 emp_id = ?
SELECT ... FROM emp WHERE
...
selectByExample(EmpExample example)按条件查询
SELECT COUNT(*) FROM emp countByExample(EmpExample example)按条件计数 WHERE …
Example 类详解(QBC 风格查询)
Example 类的作用
Example 类是 MBG 自动生成的查询条件构造器,采用 QBC(Query By Criteria) 风格,通过链式调用构建 WHERE 条件,避免手写 SQL。
Example 类结构
public class EmpExample {
protected String orderByClause; // 排序字段
protected boolean distinct; // 是否去重
protected List<Criteria> oredCriteria; // OR 条件列表
// 设置排序
public void setOrderByClause(String orderByClause) { ... }
// 设置去重
public void setDistinct(boolean distinct) { ... }
// 获取 OR 条件列表
public List<Criteria> getOredCriteria() { ... }
// 创建一个 OR 条件(相当于 OR (...))
public Criteria or() { ... }
// 添加一个 Criteria 到 OR 列表
public void or(Criteria criteria) { ... }
// 创建一个 Criteria(相当于一个 AND 条件组)
public Criteria createCriteria() { ... }
// = createCriteria(),内部调用
protected Criteria createCriteriaInternal() { ... }
// 清空所有条件
public void clear() { ... }
// ====== Criteria 内部类 ======
public abstract static class GeneratedCriteria {
// 等于
public Criteria andEmpIdEqualTo(Integer value) { ... }
// 不等于
public Criteria andEmpIdNotEqualTo(Integer value) { ... }
// 大于
public Criteria andEmpIdGreaterThan(Integer value) { ... }
// 大于等于
public Criteria andEmpIdGreaterThanOrEqualTo(Integer value) { ... }
// 小于
public Criteria andEmpIdLessThan(Integer value) { ... }
// 小于等于
public Criteria andEmpIdLessThanOrEqualTo(Integer value) { ... }
// IN
public Criteria andEmpIdIn(List<Integer> values) { ... }
// NOT IN
public Criteria andEmpIdNotIn(List<Integer> values) { ... }
// BETWEEN
public Criteria andEmpIdBetween(Integer value1, Integer value2) { ... }
// NOT BETWEEN
public Criteria andEmpIdNotBetween(Integer value1, Integer value2) { ... }
// LIKE
public Criteria andEmpNameLike(String value) { ... }
// NOT LIKE
public Criteria andEmpNameNotLike(String value) { ... }
// IS NULL
public Criteria andEmpNameIsNull() { ... }
// IS NOT NULL
public Criteria andEmpNameIsNotNull() { ... }
// ... 每个字段都有以上方法
}
// ====== Criteria 继承类 ======
public static class Criteria extends GeneratedCriteria {
// ...
}
}Example 常用查询示例
// ====== 1. 查询所有 ======
List<Emp> allEmps = empMapper.selectByExample(new EmpExample());
// ====== 2. 按主键查询 ======
Emp emp = empMapper.selectByPrimaryKey(1);
// ====== 3. 等值查询:gender = 'M' ======
EmpExample example = new EmpExample();
example.createCriteria().andGenderEqualTo("M");
List<Emp> males = empMapper.selectByExample(example);
// ====== 4. 多条件 AND 查询 ======
// WHERE gender = 'M' AND salary > 10000 AND dept_id = 1
EmpExample example = new EmpExample();
example.createCriteria()
.andGenderEqualTo("M")
.andSalaryGreaterThan(new BigDecimal("10000"))
.andDeptIdEqualTo(1);
List<Emp> result = empMapper.selectByExample(example);
// ====== 5. OR 条件查询 ======
// WHERE (gender = 'M' AND salary > 15000) OR (gender = 'F' AND salary > 12000)
EmpExample example = new EmpExample();
example.createCriteria()
.andGenderEqualTo("M")
.andSalaryGreaterThan(new BigDecimal("15000"));
example.or()
.andGenderEqualTo("F")
.andSalaryGreaterThan(new BigDecimal("12000"));
List<Emp> result = empMapper.selectByExample(example);
// ====== 6. LIKE 查询 ======
// WHERE emp_name LIKE '%张%'
EmpExample example = new EmpExample();
example.createCriteria().andEmpNameLike("%张%");
List<Emp> result = empMapper.selectByExample(example);
// ====== 7. IN 查询 ======
// WHERE dept_id IN (1, 2, 3)
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdIn(Arrays.asList(1, 2, 3));
List<Emp> result = empMapper.selectByExample(example);
// ====== 8. BETWEEN 查询 ======
// WHERE salary BETWEEN 8000 AND 20000
EmpExample example = new EmpExample();
example.createCriteria()
.andSalaryBetween(new BigDecimal("8000"), new BigDecimal("20000"));
List<Emp> result = empMapper.selectByExample(example);
// ====== 9. IS NULL 查询 ======
// WHERE email IS NULL
EmpExample example = new EmpExample();
example.createCriteria().andEmailIsNull();
List<Emp> result = empMapper.selectByExample(example);
// ====== 10. 排序 ======
// ORDER BY salary DESC
EmpExample example = new EmpExample();
example.setOrderByClause("salary DESC");
List<Emp> result = empMapper.selectByExample(example);
// ====== 11. 去重 ======
EmpExample example = new EmpExample();
example.setDistinct(true);
example.setOrderByClause("dept_id");
List<Emp> result = empMapper.selectByExample(example);
// ====== 12. 条件删除 ======
// DELETE FROM emp WHERE dept_id = 3
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdEqualTo(3);
empMapper.deleteByExample(example);
// ====== 13. 条件更新 ======
// UPDATE emp SET salary = 20000 WHERE dept_id = 1
EmpExample example = new EmpExample();
example.createCriteria().andDeptIdEqualTo(1);
Emp record = new Emp();
record.setSalary(new BigDecimal("20000"));
empMapper.updateByExampleSelective(record, example);
// ====== 14. 条件计数 ======
EmpExample example = new EmpExample();
example.createCriteria().andGenderEqualTo("M");
long count = empMapper.countByExample(example);Example 使用对照表
| 需求 | Example 方法 | SQL 操作符 |
|---|---|---|
| 等于 | andXxxEqualTo(value) | = |
| 不等于 | andXxxNotEqualTo(value) | <> |
| 大于 | andXxxGreaterThan(value) | > |
| 大于等于 | andXxxGreaterThanOrEqualTo(value) | >= |
| 小于 | andXxxLessThan(value) | < |
| 小于等于 | andXxxLessThanOrEqualTo(value) | <= |
| 模糊匹配 | andXxxLike(value) | LIKE |
| 非模糊匹配 | andXxxNotLike(value) | NOT LIKE |
| 集合包含 | andXxxIn(list) | IN |
| 集合不包含 | andXxxNotIn(list) | NOT IN |
| 区间匹配 | andXxxBetween(v1, v2) | BETWEEN |
| 为空 | andXxxIsNull() | IS NULL |
| 非空 | andXxxIsNotNull() | IS NOT NULL |
| 添加 OR 条件组 | example.or().andXxx...() | OR |
| 排序 | example.setOrderByClause("xxx DESC") | ORDER BY |
| 去重 | example.setDistinct(true) | DISTINCT |
自定义 CommentGenerator(注释生成器)
默认注释的问题
MBG 默认生成的注释格式:
/**
* This field was generated by MyBatis Generator.
* This field corresponds to the database column emp.emp_name
*
* @mbg.generated
*/
private String empName;
问题:注释无用、没有数据库字段注释、没有日期格式。
自定义 CommentGenerator
package com.example.generator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.internal.DefaultCommentGenerator;
import java.util.Properties;
/**
* 自定义注释生成器
* 生成数据库字段注释到 Java 实体类中
*/
public class CustomCommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments;
@Override
public void addConfigurationProperties(Properties properties) {
super.addConfigurationProperties(properties);
this.addRemarkComments = Boolean.parseBoolean(
properties.getProperty("addRemarkComments"));
}
/**
* 给字段添加注释(数据库列注释)
*/
@Override
public void addFieldComment(Field field,
IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
if (addRemarkComments) {
String remark = introspectedColumn.getRemarks();
if (remark != null && !remark.trim().isEmpty()) {
field.addJavaDocLine("/**");
field.addJavaDocLine(" * " + remark);
field.addJavaDocLine(" */");
}
}
}
/**
* 给类添加注释(表注释)
*/
@Override
public void addClassComment(InnerClass innerClass,
IntrospectedTable introspectedTable) {
String remark = introspectedTable.getRemarks();
innerClass.addJavaDocLine("/**");
if (remark != null && !remark.trim().isEmpty()) {
innerClass.addJavaDocLine(" * " + remark);
}
innerClass.addJavaDocLine(" * 对应数据库表: " +
introspectedTable.getFullyQualifiedTable());
innerClass.addJavaDocLine(" */");
}
/**
* 给 getter 方法添加注释
*/
@Override
public void addGetterComment(Method method,
IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
// 不生成 getter 注释(保持简洁)
}
/**
* 给 setter 方法添加注释
*/
@Override
public void addSetterComment(Method method,
IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
// 不生成 setter 注释(保持简洁)
}
}在配置文件中使用自定义注释生成器
<context id="mysqlContext" targetRuntime="MyBatis3">
<!-- 使用自定义注释生成器 -->
<commentGenerator type="com.example.generator.CustomCommentGenerator">
<property name="suppressAllComments" value="false"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- ... 其他配置 ... -->
</context>生成的实体类效果
/**
* 员工表
* 对应数据库表: mybatis_demo.emp
*/
public class Emp {
/**
* 员工ID
*/
private Integer empId;
/**
* 员工姓名
*/
private String empName;
/**
* 性别 M-男 F-女
*/
private String gender;
// ... getter/setter
}自定义 Plugin 插件
Plugin 机制概述
MBG 通过 Plugin 插件机制 实现代码生成过程的扩展。插件可以拦截生成过程,修改或增加生成的代码。
MBG 生成流程
↓
┌─────────────────────────────────┐
│ 阶段1: Model Class 生成 │ ← Plugin 可拦截
├─────────────────────────────────┤
│ 阶段2: SQL Map (XML) 生成 │ ← Plugin 可拦截
├─────────────────────────────────┤
│ 阶段3: Client (Mapper) 生成 │ ← Plugin 可拦截
├─────────────────────────────────┤
│ 阶段4: Example Class 生成 │ ← Plugin 可拦截
└─────────────────────────────────┘内置插件列表
| 插件类 | 功能 |
|---|---|
| SerializablePlugin | 让实体类实现 Serializable 接口 |
| ToStringPlugin | 生成 toString() 方法 |
| EqualsAndHashCodePlugin | 生成 equals() 和 hashCode() 方法 |
| RowBoundsPlugin | 生成分页方法(基于 RowBounds,不推荐 MySQL 使用) |
| VirtualKeyPlugin | 虚拟主键插件 |
| FluentBuilderMethodsPlugin | 生成链式 setter(返回 this) |
| MapperNamePlugin | 自定义 Mapper 名称 |
| RenameExampleClassPlugin | 重命名 Example 类 |
| CaseInsensitiveLikePlugin | 大小写不敏感 LIKE 查询 |
| UnmergeableXmlMapperPlugin | XML 不合并,直接覆盖 |
自定义 Plugin 示例:Lombok 插件
让生成的实体类使用 Lombok 注解,替代冗长的 getter/setter:
package com.example.generator;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import java.util.List;
/**
* 自定义 Lombok 插件
* 让生成的实体类使用 @Data 注解,去掉 getter/setter
*/
public class LombokPlugin extends PluginAdapter {
@Override
public boolean validate(List<String> warnings) {
return true;
}
/**
* 给实体类添加 Lombok 注解
*/
@Override
public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
IntrospectedTable introspectedTable)
{
// 添加 @Data 注解
topLevelClass.addImportedType("lombok.Data");
topLevelClass.addAnnotation("@Data");
// 添加 @Builder 注解(可选)
topLevelClass.addImportedType("lombok.Builder");
topLevelClass.addAnnotation("@Builder");
// 添加 @NoArgsConstructor
topLevelClass.addImportedType("lombok.NoArgsConstructor");
topLevelClass.addAnnotation("@NoArgsConstructor");
// 添加 @AllArgsConstructor
topLevelClass.addImportedType("lombok.AllArgsConstructor");
topLevelClass.addAnnotation("@AllArgsConstructor");
return true;
}
/**
* 给主键类添加 Lombok 注解(如果有)
*/
@Override
public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass,
IntrospectedTable introspectedTable)
{
topLevelClass.addImportedType("lombok.Data");
topLevelClass.addAnnotation("@Data");
return true;
}
/**
* 禁止生成 getter 方法(由 Lombok @Data 提供)
*/
@Override
public boolean modelGetterMethodGenerated(Method method,
TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable,
ModelClassType modelClassType) {
return false; // false = 不生成 getter
}
/**
* 禁止生成 setter 方法
*/
@Override
public boolean modelSetterMethodGenerated(Method method,
TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable,
ModelClassType modelClassType) {
return false; // false = 不生成 setter
}
}配置中使用自定义插件
<context id="mysqlContext" targetRuntime="MyBatis3">
<!-- 内置插件 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
<!-- ★ 自定义 Lombok 插件 -->
<plugin type="com.example.generator.LombokPlugin"/>
<!-- ★ 重命名 Example 类为 Query -->
<plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
<property name="searchString" value="Example$"/>
<property name="replaceString" value="Query"/>
</plugin>
<!-- 自定义注释生成器 -->
<commentGenerator type="com.example.generator.CustomCommentGenerator">
<property name="suppressAllComments" value="false"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- ... 其他配置 ... -->
</context>自定义 Plugin 示例:Mapper 继承通用接口
package com.example.generator;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import java.util.List;
/**
* 让所有生成的 Mapper 接口继承自定义的 BaseMapper
*/
public class BaseMapperPlugin extends PluginAdapter {
private String baseMapperPackage;
@Override
public boolean validate(List<String> warnings) {
baseMapperPackage = properties.getProperty("baseMapperPackage");
return baseMapperPackage != null && !baseMapperPackage.trim().isEmpty();
}
@Override
public boolean clientGenerated(Interface interfaze,
TopLevelClass topLevelClass,
IntrospectedTable introspectedTable) {
// 导入 BaseMapper
FullyQualifiedJavaType baseMapperType =
new FullyQualifiedJavaType(baseMapperPackage);
interfaze.addImportedType(baseMapperType);
// 让 Mapper 继承 BaseMapper<实体类>
FullyQualifiedJavaType entityType =
new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
interfaze.addSuperInterface(
new FullyQualifiedJavaType(baseMapperPackage + "<" +
entityType.getShortName() + ">"));
return true;
}
}
<plugin type="com.example.generator.BaseMapperPlugin">
<property name="baseMapperPackage" value="com.example.mapper.BaseMapper"/>
</plugin>MyBatis-Plus 代码生成器(3.5.x+)
MP Generator 概述
MyBatis-Plus 代码生成器比 MBG 功能更强大,能生成:
- Entity 实体类(支持 Lombok / Swagger 注解)
- Mapper 接口(继承 BaseMapper)
- Mapper XML 映射文件
- Service 接口 + Service 实现类
- Controller 控制器(含 RESTful 接口)
依赖配置
<dependencies>
<!-- MyBatis-Plus(包含 MyBatis) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MP 代码生成器核心 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 模板引擎(Freemarker / Velocity / Beetl 任选其一) -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Lombok(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<!-- Swagger / SpringDoc(可选) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>快速生成器(FastAutoGenerator)
MyBatis-Plus 3.5.3+ 提供了 FastAutoGenerator,链式配置一行搞定:
package com.example.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class MpGenerator {
public static void main(String[] args) {
FastAutoGenerator.create(
"jdbc:mysql://localhost:3306/mybatis_demo?
useSSL=false&serverTimezone=Asia/Shanghai",
"root",
"123456")
// ====== 全局配置 ======
.globalConfig(builder -> {
builder.author("example") // 作者
.outputDir("src/main/java") // 输出目录
.enableSwagger() // 开启 Swagger 注解
.commentDate("yyyy-MM-dd"); // 注释日期格式
})
// ====== 包配置 ======
.packageConfig(builder -> {
builder.parent("com.example") // 父包名
.entity("entity") // 实体类包名
.mapper("mapper") // Mapper 包名
.service("service") // Service 包名
.serviceImpl("service.impl") // Service 实现包名
.controller("controller") // Controller 包名
.xml("mapper") // XML 资源路径
.pathInfo(Collections.singletonMap(
OutputFile.xml,
"src/main/resources/mapper")); // XML 输出目录
})
// ====== 策略配置 ======
.strategyConfig(builder -> {
builder.addInclude("emp", "dept") // 表名(可变参数)
.addTablePrefix("t_", "sys_") // 过滤表前缀
.entityBuilder()
.naming(NamingStrategy.underline_to_camel) // 下划线转驼峰
.columnNaming(NamingStrategy.underline_to_camel)
.enableLombok() // 使用 Lombok
.enableTableFieldAnnotation() // 生成字段注解
.logicDeleteColumnName("deleted") // 逻辑删除字段
.versionColumnName("version") // 乐观锁字段
.idType(IdType.AUTO) // 主键策略
.formatFileName("%s") // 实体类名格式
.mapperBuilder()
.enableMapperAnnotation() // 生成 @Mapper 注解
.formatMapperFileName("%sMapper") // Mapper 文件名格式
.formatXmlFileName("%sMapper") // XML 文件名格式
.serviceBuilder()
.formatServiceFileName("%sService") // Service 接口名
.formatServiceImplFileName("%sServiceImpl") // Service 实现名
.controllerBuilder()
.enableRestStyle(); // 生成 @RestController
})
// ====== 模板引擎 ======
.templateEngine(new FreemarkerTemplateEngine())
// ====== 执行 ======
.execute();
System.out.println("✅ MyBatis-Plus 代码生成完成!");
}
}生成的目录结构
src/main/java/com/example/
├── entity/
│ ├── Emp.java ← @Data + @TableName + 字段注解
│ └── Dept.java
├── mapper/
│ ├── EmpMapper.java ← extends BaseMapper<Emp>
│ └── DeptMapper.java
├── service/
│ ├── EmpService.java ← extends IService<Emp>
│ └── impl/
│ └── EmpServiceImpl.java ← extends ServiceImpl<EmpMapper, Emp>
└── controller/
├── EmpController.java ← @RestController
└── DeptController.java
src/main/resources/
└── mapper/
├── EmpMapper.xml
└── DeptMapper.xmlMyBatis-Plus Generator 配置详解
完整 AutoGenerator 配置(非 Fast 模式)
package com.example.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
public class MpFullGenerator {
public static void main(String[] args) {
// ====== 1. 数据源配置 ======
DataSourceConfig dataSourceConfig = DataSourceConfig.builder(
"jdbc:mysql://localhost:3306/mybatis_demo?
useSSL=false&serverTimezone=Asia/Shanghai",
"com.mysql.cj.jdbc.Driver",
"root",
"123456")
.build();
// ====== 2. 全局配置 ======
GlobalConfig globalConfig = GlobalConfig.builder()
.author("example")
.outputDir("src/main/java")
.enableSwagger() // Swagger 注解
.disableOpenDir() // 生成后不自动打开目录
.commentDate("yyyy-MM-dd")
.dateType(DateType.TIME_PACK) // 使用 java.time 包
.build();
// ====== 3. 包配置 ======
PackageConfig packageConfig = PackageConfig.builder()
.parent("com.example")
.moduleName("system") // 模块名(会加到包路径中)
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.xml("mapper.xml")
.pathInfo(java.util.Collections.singletonMap(
OutputFile.xml,
"src/main/resources/mapper"))
.build();
// ====== 4. 模板配置 ======
TemplateConfig templateConfig = TemplateConfig.builder()
.entity("templates/entity.java") // 使用自定义模板
.mapper("templates/mapper.java")
.xml("templates/mapper.xml")
.service("templates/service.java")
.serviceImpl("templates/serviceImpl.java")
.controller("templates/controller.java")
.build();
// ====== 5. 策略配置 ======
StrategyConfig strategyConfig = StrategyConfig.builder()
.addInclude("emp", "dept") // 要生成的表
.addTablePrefix("t_", "sys_") // 过滤表前缀
.addTableSuffix("_tab") // 过滤表后缀
// 实体策略
.entityBuilder()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.enableLombok() // Lombok 注解
.enableTableFieldAnnotation() // @TableField 注解
.enableActiveRecord() // ActiveRecord 模式
.enableFileOverride() // 覆盖已有文件
.logicDeleteColumnName("deleted") // 逻辑删除
.versionColumnName("version") // 乐观锁
.idType(IdType.AUTO) // 主键策略
.formatFileName("%s") // 实体类名格式
.superClass("com.example.BaseEntity") // 父类
// Mapper 策略
.mapperBuilder()
.enableMapperAnnotation() // @Mapper 注解
.enableBaseResultMap() // 生成 BaseResultMap
.enableBaseColumnList() // 生成 BaseColumnList
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper")
// Service 策略
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")
.superServiceClass("com.example.BaseService")
.superServiceImplClass("com.example.BaseServiceImpl")
// Controller 策略
.controllerBuilder()
.enableRestStyle() // @RestController
.enableHyphenStyle() // URL 连字符风格
.formatFileName("%sController")
.build();
// ====== 6. 注入配置(自定义变量) ======
InjectionConfig injectionConfig = InjectionConfig.builder()
.customMap(java.util.Map.of(
"author", "example",
"version", "1.0.0"))
.build();
// ====== 7. 组装执行 ======
AutoGenerator generator = new AutoGenerator(dataSourceConfig);
generator.global(globalConfig);
generator.packageInfo(packageConfig);
generator.template(templateConfig);
generator.strategy(strategyConfig);
generator.injection(injectionConfig);
generator.templateEngine(new FreemarkerTemplateEngine());
generator.execute();
System.out.println("✅ 代码生成完成!");
}
}IdType 主键策略
IdType 值 | 说明 | 常见场景 |
|---|---|---|
AUTO | 使用数据库自增主键 | MySQL AUTO_INCREMENT 等 |
NONE | 不指定局部策略,按全局配置处理 | 由项目统一配置 |
INPUT | 插入前由开发者手动赋值 | 业务主键 |
ASSIGN_ID | 使用默认标识符生成器分配长整型 ID | 分布式系统常用 |
ASSIGN_UUID | 分配不带连字符的 UUID 字符串 | 字符串主键 |
NamingStrategy 命名策略
| 值 | 说明 | 示例 |
|---|---|---|
underline_to_camel | 下划线命名转驼峰命名 | emp_name 转为 empName |
no_change | 保持数据库名称不变 | emp_name 保持为 emp_name |
数据源类型映射配置
// MySQL 8.x 类型映射自定义
DataSourceConfig dataSourceConfig = DataSourceConfig.builder(
url, driver, username, password)
.typeConvert(new ITypeConvert() {
@Override
public IColumnType executeTypeConvert(TableInfo tableInfo, String fieldName,
String fieldType) {
// 自定义类型映射
if ("tinyint(1)".equalsIgnoreCase(fieldType)) {
return DbColumnType.BOOLEAN;
}
if ("json".equalsIgnoreCase(fieldType)) {
return DbColumnType.OBJECT;
}
// 默认使用 MP 内置映射
return TypeConverts.useDefault(tableInfo, fieldName, fieldType);
}
})
.build();模板自定义
自定义实体类模板
MP 使用 Freemarker 模板引擎,默认模板位于 mybatis-plus-generator jar 包中的 templates/ 目录。
自定义 entity.java.ftl 模板:
package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger2>
import io.swagger.v3.oas.annotations.media.Schema;
</#if>
<#if entityLombokAnnotations>
import lombok.Data;
import lombok.experimental.Accessors;
</#if>
/**
* <p>
* ${table.comment!}
* </p>
*
* @author ${author}
* @since ${date}
*/
<#if entityLombokAnnotations>
@Data
@Accessors(chain = true)
</#if>
<#if swagger2>
@Schema(description = "${table.comment!}")
</#if>
@TableName("${schemaName}${table.name}")
public class ${entity} implements Serializable {
private static final long serialVersionUID = 1L;
<#-- ---------- BEGIN 字段循环遍历 ---------->
<#list table.fields as field>
<#if field.keyFlag>
<#if field.keyIdentityFlag>
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
<#elseif idType??>
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
<#else>
@TableId(value = "${field.annotationColumnName}", type = IdType.ASSIGN_ID)
</#if>
<#elseif field.fill??>
<#-- 通用填充字段 -->
@TableField(value = "${field.annotationColumnName}", fill =
FieldFill.${field.fill})
<#elseif !field.propertyType?ends_with("Serializable")>
@TableField("${field.annotationColumnName}")
</#if>
<#if swagger2>
@Schema(description = "${field.comment!}")
</#if>
private ${field.propertyType} ${field.propertyName};
</#list>
<#------------ END 字段循环遍历 ---------->
<#if !entityLombokAnnotations>
<#list table.fields as field>
<#if field.propertyType == "boolean">
<#assign getprefix="is"/>
<#else>
<#assign getprefix="get"/>
</#if>
public ${field.propertyType} ${getprefix}${field.capitalPropertyName}() {
return ${field.propertyName};
}
public void set${field.capitalPropertyName}(${field.propertyType}
${field.propertyName}) {
this.${field.propertyName} = ${field.propertyName};
}
</#list>
</#if>
}使用自定义模板
TemplateConfig templateConfig = TemplateConfig.builder()
.entity("/templates/my-entity.java") // 自定义模板路径
.mapper("/templates/my-mapper.java")
.xml("/templates/my-mapper.xml")
.service("/templates/my-service.java")
.serviceImpl("/templates/my-serviceImpl.java")
.controller("/templates/my-controller.java")
.build();
将自定义模板放在 src/main/resources/templates/ 目录下。
自定义 Controller 模板(RESTful 风格)
package ${package.Controller};
import ${package.Entity}.${entity};
import ${package.Service}.${entity}Service;
<#if swagger2>
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
</#if>
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* ${table.comment!} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
<#if swagger2>
@Tag(name = "${table.comment!}")
</#if>
@RestController
@RequestMapping("/${table.entityPath}")
public class ${entity}Controller {
@Autowired
private ${entity}Service ${table.entityPath}Service;
@Operation(summary = "查询所有")
@GetMapping
public List<${entity}> list() {
return ${table.entityPath}Service.list();
}
@Operation(summary = "根据ID查询")
@GetMapping("/{id}")
public ${entity} getById(@PathVariable ${entity} ${table.entityPath}) {
return ${table.entityPath}Service.getById(${table.entityPath});
}
@Operation(summary = "新增")
@PostMapping
public boolean save(@RequestBody ${entity} ${table.entityPath}) {
return ${table.entityPath}Service.save(${table.entityPath});
}
@Operation(summary = "修改")
@PutMapping
public boolean update(@RequestBody ${entity} ${table.entityPath}) {
return ${table.entityPath}Service.updateById(${table.entityPath});
}
@Operation(summary = "根据ID删除")
@DeleteMapping("/{id}")
public boolean delete(@PathVariable ${entity} ${table.entityPath}) {
return ${table.entityPath}Service.removeById(${table.entityPath});
}
}Spring Boot 集成逆向工程
Spring Boot 项目中集成 MBG
pom.xml 配置:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- MBG 插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<executions>
<!-- 可选:绑定到 generate-sources 阶段自动执行 -->
<execution>
<id>Generate MyBatis Artifacts</id>
<goals>
<goal>generate</goal>
</goals>
<!-- 取消注释则每次构建自动生成 -->
<!-- <phase>generate-sources</phase> -->
</execution>
</executions>
<configuration>
<configurationFile>
src/main/resources/generator/generatorConfig.xml
</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>Spring Boot + MyBatis-Plus 集成
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?
useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0生成的实体类使用示例
// ====== Emp.java(MP 生成的实体类)======
@Schema(description = "员工表")
@TableName("emp")
@Data
@Accessors(chain = true)
public class Emp implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "员工ID")
@TableId(value = "emp_id", type = IdType.AUTO)
private Integer empId;
@Schema(description = "员工姓名")
@TableField("emp_name")
private String empName;
@Schema(description = "性别 M-男 F-女")
private String gender;
@Schema(description = "邮箱")
private String email;
@Schema(description = "工资")
private BigDecimal salary;
@Schema(description = "所属部门ID")
@TableField("dept_id")
private Integer deptId;
@Schema(description = "入职日期")
private LocalDate hireDate;
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Schema(description = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
// ====== EmpMapper.java ======
@Mapper
public interface EmpMapper extends BaseMapper<Emp> {
// BaseMapper 已提供 insert/delete/update/select 等方法
// 可以在这里添加自定义 SQL
}
// ====== EmpService.java ======
public interface EmpService extends IService<Emp> {
// IService 已提供通用业务方法
// 可以在这里添加自定义业务方法
}
// ====== EmpServiceImpl.java ======
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
// ServiceImpl 已实现 IService 的所有方法
}
// ====== EmpController.java ======
@Tag(name = "员工管理")
@RestController
@RequestMapping("/emp")
public class EmpController {
@Autowired
private EmpService empService;
@Operation(summary = "分页查询")
@GetMapping("/page")
public Page<Emp> page(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
return empService.page(new Page<>(current, size));
}
@Operation(summary = "新增员工")
@PostMapping
public boolean save(@RequestBody Emp emp) {
return empService.save(emp);
}
@Operation(summary = "根据ID查询")
@GetMapping("/{id}")
public Emp getById(@PathVariable Integer id) {
return empService.getById(id);
}
@Operation(summary = "更新员工")
@PutMapping
public boolean update(@RequestBody Emp emp) {
return empService.updateById(emp);
}
@Operation(summary = "删除员工")
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) {
return empService.removeById(id);
}
}多模块项目中的逆向工程
多模块项目结构
my-project/
├── pom.xml ← 父 POM
├── my-project-common/ ← 通用模块
│ └── pom.xml
├── my-project-entity/ ← 实体模块
│ ├── pom.xml
│ └── src/main/java/com/example/entity/
├── my-project-mapper/ ← Mapper 模块
│ ├── pom.xml
│ └── src/main/java/com/example/mapper/
├── my-project-service/ ← Service 模块
│ ├── pom.xml
│ └── src/main/java/com/example/service/
├── my-project-web/ ← Web 模块(启动模块)
│ ├── pom.xml
│ └── src/main/resources/
│ ├── generatorConfig.xml
│ └── application.yml
└── my-project-generator/ ← 代码生成器模块(独立)
├── pom.xml
└── src/main/java/com/example/Generator.java多模块 MBG 配置
<!-- generatorConfig.xml 放在 my-project-generator 模块中 -->
<generatorConfiguration>
<context id="multiModule" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<commentGenerator>
<property name="suppressAllComments" value="false"/>
</commentGenerator>
<jdbcConnection driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
<javaTypeResolver>
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
<!-- 实体类输出到 entity 模块 -->
<javaModelGenerator
targetPackage="com.example.entity"
targetProject="../my-project-entity/src/main/java">
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- XML 输出到 web 模块的 resources -->
<sqlMapGenerator
targetPackage="mapper"
targetProject="../my-project-web/src/main/resources">
</sqlMapGenerator>
<!-- Mapper 接口输出到 mapper 模块 -->
<javaClientGenerator
type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="../my-project-mapper/src/main/java">
</javaClientGenerator>
<table tableName="emp" domainObjectName="Emp">
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>关键: targetProject 使用相对路径 ../模块名/src/main/java 指向其他模块的源码目录。
多模块 MP Generator 配置
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> builder
.author("example")
.outputDir(System.getProperty("user.dir") + "/my-project-generator/src/main/java")
)
.packageConfig(builder -> builder
.parent("com.example")
// 实体类输出到 entity 模块
.entity("entity")
// Mapper 输出到 mapper 模块
.mapper("mapper")
.pathInfo(java.util.Map.of(
OutputFile.entity, "../my-project-entity/src/main/java",
OutputFile.mapper, "../my-project-mapper/src/main/java",
OutputFile.service, "../my-project-service/src/main/java",
OutputFile.serviceImpl, "../my-project-service/src/main/java",
OutputFile.controller, "../my-project-web/src/main/java",
OutputFile.xml, "../my-project-web/src/main/resources/mapper"
))
)
.strategyConfig(builder -> builder
.addInclude("emp", "dept")
.entityBuilder().enableLombok()
)
.execute();实战案例一:MBG 完整项目
项目搭建
# 1. 创建 Maven 项目
mvn archetype:generate -DgroupId=com.example -DartifactId=mbg-demo -DarchetypeArtifactId=maven-archetype-quickstart
# 2. 进入项目目录
cd mbg-demo
# 3. 添加依赖(参考 III 章节 pom.xml)
完整配置文件
generatorConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="generator.properties"/>
<context id="mysqlContext" targetRuntime="MyBatis3"
defaultModelType="conditional">
<!-- 插件 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<plugin type="org.mybatis.generator.plugins.EqualsAndHashCodePlugin"/>
<!-- 重命名 Example 为 Query -->
<plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
<property name="searchString" value="Example$"/>
<property name="replaceString" value="Query"/>
</plugin>
<!-- 注释生成器 -->
<commentGenerator>
<property name="suppressAllComments" value="false"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- 数据库连接 -->
<jdbcConnection driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
<!-- 类型解析 -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
<!-- 实体类 -->
<javaModelGenerator
targetPackage="com.example.entity"
targetProject="src/main/java">
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- XML -->
<sqlMapGenerator
targetPackage="mapper"
targetProject="src/main/resources">
</sqlMapGenerator>
<!-- Mapper -->
<javaClientGenerator
type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
</javaClientGenerator>
<!-- 表配置 -->
<table tableName="emp" domainObjectName="Emp"
enableCountByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
enableUpdateByExample="true">
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="dept" domainObjectName="Dept">
<generatedKey column="dept_id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>执行生成
# 方式一:Maven 命令
mvn mybatis-generator:generate
# 方式二:运行 Java 类
# 直接运行 Generator.java 的 main 方法
编写测试
package com.example.test;
import com.example.entity.Emp;
import com.example.entity.EmpQuery;
import com.example.mapper.EmpMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.List;
public class EmpMapperTest {
private SqlSession session;
private EmpMapper empMapper;
@Before
public void setUp() throws Exception {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
session = factory.openSession();
empMapper = session.getMapper(EmpMapper.class);
}
@After
public void tearDown() {
if (session != null) {
session.close();
}
}
// 测试1:插入
@Test
public void testInsert() {
Emp emp = new Emp();
emp.setEmpName("赵六");
emp.setGender("M");
emp.setEmail("zhaoliu@qq.com");
emp.setSalary(new BigDecimal("13000"));
emp.setDeptId(1);
int rows = empMapper.insert(emp);
System.out.println("插入行数: " + rows);
System.out.println("主键回填: " + emp.getEmpId());
session.commit();
}
// 测试2:按主键查询
@Test
public void testSelectByPrimaryKey() {
Emp emp = empMapper.selectByPrimaryKey(1);
System.out.println(emp);
}
// 测试3:条件查询
@Test
public void testSelectByExample() {
EmpQuery query = new EmpQuery();
query.createCriteria()
.andGenderEqualTo("M")
.andSalaryGreaterThan(new BigDecimal("10000"));
query.setOrderByClause("salary DESC");
List<Emp> list = empMapper.selectByExample(query);
list.forEach(System.out::println);
}
// 测试4:选择性更新
@Test
public void testUpdateByPrimaryKeySelective() {
Emp emp = new Emp();
emp.setEmpId(1);
emp.setSalary(new BigDecimal("20000"));
int rows = empMapper.updateByPrimaryKeySelective(emp);
System.out.println("更新行数: " + rows);
session.commit();
}
// 测试5:条件删除
@Test
public void testDeleteByExample() {
EmpQuery query = new EmpQuery();
query.createCriteria().andDeptIdIsNull();
int rows = empMapper.deleteByExample(query);
System.out.println("删除行数: " + rows);
session.commit();
}
// 测试6:条件计数
@Test
public void testCountByExample() {
EmpQuery query = new EmpQuery();
query.createCriteria().andGenderEqualTo("M");
long count = empMapper.countByExample(query);
System.out.println("男性员工数量: " + count);
}
}实战案例二:MyBatis-Plus Generator 完整项目
项目搭建
<!-- pom.xml 关键依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>生成器代码
package com.example.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.HashMap;
import java.util.Map;
public class CodeGenerator {
public static void main(String[] args) {
// 数据库配置
String url = "jdbc:mysql://localhost:3306/mybatis_demo" +
"?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8";
String username = "root";
String password = "123456";
// 输出目录
String projectPath = System.getProperty("user.dir");
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> builder
.author("example")
.outputDir(projectPath + "/src/main/java")
.enableSwagger()
.commentDate("yyyy-MM-dd")
.disableOpenDir()
)
.packageConfig(builder -> {
Map<OutputFile, String> pathInfo = new HashMap<>();
pathInfo.put(OutputFile.xml, projectPath +
"/src/main/resources/mapper");
builder
.parent("com.example")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.pathInfo(pathInfo);
})
.strategyConfig(builder -> builder
.addInclude("emp", "dept") // 要生成的表
.addTablePrefix("t_", "sys_") // 去掉表前缀
.entityBuilder()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.enableLombok()
.enableTableFieldAnnotation()
.idType(IdType.AUTO)
.logicDeleteColumnName("deleted")
.versionColumnName("version")
.formatFileName("%s")
.mapperBuilder()
.enableMapperAnnotation()
.enableBaseResultMap()
.enableBaseColumnList()
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper")
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")
.controllerBuilder()
.enableRestStyle()
.enableHyphenStyle()
.formatFileName("%sController")
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
System.out.println("✅ 代码生成完成!");
}
}自动填充处理器
package com.example.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 自动填充处理器
* 配合实体类 @TableField(fill = FieldFill.INSERT) 使用
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 新增时自动填充
this.strictInsertFill(metaObject, "createTime",
LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime",
LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时自动填充
this.strictUpdateFill(metaObject, "updateTime",
LocalDateTime.class, LocalDateTime.now());
}
}分页配置
package com.example.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(
new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}测试使用
package com.example.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.Emp;
import com.example.service.EmpService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "员工管理")
@RestController
@RequestMapping("/emp")
public class EmpController {
@Autowired
private EmpService empService;
@Operation(summary = "分页查询")
@GetMapping("/page")
public Page<Emp> page(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
return empService.page(new Page<>(current, size));
}
@Operation(summary = "条件分页查询")
@GetMapping("/search")
public Page<Emp> search(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String name,
@RequestParam(required = false) String gender,
@RequestParam(required = false) Integer deptId) {
Page<Emp> page = new Page<>(current, size);
return empService.lambdaQuery()
.like(name != null && !name.isEmpty(), Emp::getEmpName, name)
.eq(gender != null && !gender.isEmpty(), Emp::getGender, gender)
.eq(deptId != null, Emp::getDeptId, deptId)
.orderByDesc(Emp::getSalary)
.page(page);
}
@Operation(summary = "新增")
@PostMapping
public boolean save(@RequestBody Emp emp) {
return empService.save(emp);
}
@Operation(summary = "批量新增")
@PostMapping("/batch")
public boolean saveBatch(@RequestBody List<Emp> list) {
return empService.saveBatch(list);
}
@Operation(summary = "根据ID查询")
@GetMapping("/{id}")
public Emp getById(@PathVariable Integer id) {
return empService.getById(id);
}
@Operation(summary = "更新")
@PutMapping
public boolean update(@RequestBody Emp emp) {
return empService.updateById(emp);
}
@Operation(summary = "根据ID删除")
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) {
return empService.removeById(id);
}
}实战案例三:自定义插件生成 Swagger 注解
需求
让 MBG 生成的实体类自动添加 Swagger/OpenAPI 注解:
@Schema(description = "员工表")
public class Emp {
@Schema(description = "员工ID")
private Integer empId;
@Schema(description = "员工姓名")
private String empName;
}
自定义 Swagger 插件
package com.example.generator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import java.util.List;
/**
* 自定义 Swagger 注解插件
* 自动给实体类和字段添加 @Schema 注解
*/
public class SwaggerAnnotationPlugin extends PluginAdapter {
private static final String SCHEMA = "io.swagger.v3.oas.annotations.media.Schema";
@Override
public boolean validate(List<String> warnings) {
return true;
}
/**
* 给实体类添加 @Schema 注解
*/
@Override
public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
IntrospectedTable introspectedTable)
{
// 导入 @Schema
topLevelClass.addImportedType(SCHEMA);
// 生成注解:@Schema(description = "表注释")
String remark = introspectedTable.getRemarks();
String description = (remark != null && !remark.trim().isEmpty())
? remark
: introspectedTable.getFullyQualifiedTable().toString();
topLevelClass.addAnnotation("@Schema(description = \"" + escape(description) +
"\")");
return true;
}
/**
* 给字段添加 @Schema 注解
*/
@Override
public boolean modelFieldGenerated(Field field,
TopLevelClass topLevelClass,
IntrospectedColumn introspectedColumn,
IntrospectedTable introspectedTable,
ModelClassType modelClassType) {
// 确保 @Schema 已导入
if (!topLevelClass.getImportedTypes().stream()
.anyMatch(t -> t.getFullyQualifiedName().equals(SCHEMA))) {
topLevelClass.addImportedType(SCHEMA);
}
// 生成字段注解
String remark = introspectedColumn.getRemarks();
String description = (remark != null && !remark.trim().isEmpty())
? remark
: introspectedColumn.getActualColumnName();
field.addAnnotation("@Schema(description = \"" + escape(description) + "\")");
return true;
}
/**
* 转义特殊字符
*/
private String escape(String s) {
return s.replace("\"", "\\\"").replace("\n", " ").trim();
}
}配置使用
<context id="mysqlContext" targetRuntime="MyBatis3">
<!-- ★ Swagger 注解插件 -->
<plugin type="com.example.generator.SwaggerAnnotationPlugin"/>
<!-- ★ Lombok 插件(配合使用) -->
<plugin type="com.example.generator.LombokPlugin"/>
<!-- Serializable -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
userId="root" password="123456">
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
<javaTypeResolver>
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.example.entity"
targetProject="src/main/java">
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
</javaClientGenerator>
<table tableName="emp" domainObjectName="Emp">
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
</table>
</context>生成的实体类效果
package com.example.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "员工表")
public class Emp implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "员工ID")
private Integer empId;
@Schema(description = "员工姓名")
private String empName;
@Schema(description = "性别 M-男 F-女")
private String gender;
@Schema(description = "邮箱")
private String email;
@Schema(description = "工资")
private BigDecimal salary;
@Schema(description = "所属部门ID")
private Integer deptId;
@Schema(description = "入职日期")
private LocalDate hireDate;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
}常见问题与排错
生成后没有表注释和字段注释
原因: MySQL 8.x 默认不返回注释信息。
解决: 在 <jdbcConnection> 中添加 <property name=“useInformationSchema” value=“true”/>。
<jdbcConnection ...>
<property name="useInformationSchema" value="true"/>
</jdbcConnection>
日期类型生成的是 Date 而不是 LocalDateTime
原因: 默认使用 java.util.Date。
解决: 在 <javaTypeResolver> 中开启 JSR-310:
<javaTypeResolver>
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
注意:此属性在 MBG 1.4.0+ 才支持。DATETIME → LocalDateTime,DATE → LocalDate,TIME → LocalTime。
文件覆盖问题:XML 文件没有完全覆盖
原因: MBG 默认对 XML 文件使用 merge(合并) 策略,而不是完全覆盖。这是为了保护你手写的自定义 SQL。
解决: 使用 UnmergeableXmlMapperPlugin 插件强制覆盖:
1<plugin type=“org.mybatis.generator.plugins.UnmergeableXmlMapperPlugin”/>
生成的包名多了一层 schema 名
原因: enableSubPackages 设为 true 时,会将 schema 名作为包的后缀。
解决: 设置 enableSubPackages=“false”:
<javaModelGenerator targetPackage="com.example.entity" ...>
<property name="enableSubPackages" value="false"/>
</javaModelGenerator>
主键没有回填(insert 后实体类中 id 为 null)
原因: 没有配置 <generatedKey>。
解决:
<table tableName="emp" domainObjectName="Emp">
<generatedKey column="emp_id" sqlStatement="MySql" identity="true"/>
</table>
Maven 插件运行时找不到驱动
原因: Maven 插件的 classpath 中没有 MySQL 驱动。
解决: 在 <plugin> 的 <dependencies> 中添加驱动:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</plugin>MySQL 关键字冲突(如desc, order, key)
原因: 表名或字段名是 MySQL 保留字。
解决: 在 context 上配置分隔符:
<context id="mysqlContext" targetRuntime="MyBatis3"
autoDelimitKeywords="true"
beginningDelimiter="`"
endingDelimiter="`">
或者在 table 上配置:
1<table tableName=“order” delimitIdentifiers=“true”>
MP Generator 生成的代码在错误的目录
原因: outputDir 路径不正确。
解决: 使用 System.getProperty(“user.dir”) 确保路径正确:
String projectPath = System.getProperty("user.dir");
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> builder
.outputDir(projectPath + "/src/main/java") // 绝对路径
)
...
MBG 生成文件中文乱码
原因: 编码不一致。
解决: 在 pom.xml 中配置编码:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
并在 Maven 插件中配置:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
MP Generator 3.5.x API 变化
问题: 从 3.4.x 升级到 3.5.x 后 API 不兼容。
主要变化:
| 3.4.x(旧) | 3.5.x(新) |
|---|---|
| new AutoGenerator() | new AutoGenerator(dataSourceConfig) |
| generator.setDataSource(config) | 构造器参数传入 |
| generator.setGlobalConfig(config) | generator.global(config) |
| generator.setStrategy(config) | generator.strategy(config) |
| generator.setPackageInfo(config) | generator.packageInfo(config) |
| generator.setTemplate(config) | generator.template(config) |
最佳实践
生成策略选择
是否需要 Service / Controller?
├── 是 → 使用 MyBatis-Plus Generator
│ (自带 Service / Controller / BaseMapper)
│
└── 否 → 使用 MyBatis Generator
(仅生成 Entity / Mapper / XML / Example)
将生成代码与自定义代码分离
核心原则:生成的代码可以随时重新生成,不要直接修改。
正确的做法:
├── 生成的 Mapper 接口 ← 保持不变(继承 BaseMapper 扩展)
├── 自定义 SQL ← 新建 CustomXxxMapper.java 或在同一 XML 中追加
└── 业务逻辑 ← 写在 Service 层
错误的做法:
├── 直接在生成的 EmpMapper.java 中添加方法 ← 重新生成会被覆盖
├── 直接在生成的 EmpMapper.xml 中修改 SQL ← merge 可能保留,但有风险
└── 直接在生成的 Emp.java 中添加逻辑 ← 重新生成会被覆盖
XML 自定义 SQL 扩展方案
<!-- EmpMapper.xml — MBG 生成的部分保持不变 -->
<!-- ====== 以下为手动追加的自定义 SQL ====== -->
<!-- 多表关联查询 -->
<select id="selectEmpWithDept" resultMap="EmpWithDeptResultMap">
SELECT e.*, d.dept_name, d.location
FROM emp e
LEFT JOIN dept d ON e.dept_id = d.dept_id
WHERE e.emp_id = #{empId}
</select>
<resultMap id="EmpWithDeptResultMap" type="com.example.dto.EmpDeptDTO">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="gender" property="gender"/>
<result column="salary" property="salary"/>
<result column="dept_name" property="deptName"/>
<result column="location" property="location"/>
</resultMap>配置管理建议
项目结构建议:
├── src/main/resources/
│ ├── generator/
│ │ ├── generatorConfig.xml ← MBG 配置
│ │ ├── generator.properties ← 数据库连接属性
│ │ └── generator-dev.properties ← 开发环境属性(可选)
│ ├── mapper/
│ │ └── EmpMapper.xml ← 生成的 XML
│ └── mybatis-config.xml生成 vs 手写决策表
| 场景 | 建议方式 |
|---|---|
| 单表 CRUD | 逆向工程生成 |
| 单表条件查询 | MBG 的 Example 或 MP 的 LambdaQueryWrapper |
| 多表 JOIN 查询 | 手写 SQL(XML 或注解) |
| 复杂统计报表 | 手写 SQL |
| 分页查询 | MP 分页插件 |
| 批量操作 | MP 的 saveBatch / updateBatchById |
速查表
MBG 配置元素速查表
| 元素 | 作用 | 必填 |
|---|---|---|
| <generatorConfiguration> | 根元素 | 是 |
| <properties> | 引入属性文件 | 否 |
| <classPathEntry> | 数据库驱动路径 | Maven 方式可省 |
| <context> | 生成上下文 | 是 |
| <plugin> | 插件配置 | 否 |
| <commentGenerator> | 注释生成器 | 否 |
| <jdbcConnection> | 数据库连接 | 是 |
| <javaTypeResolver> | 类型解析器 | 否 |
| <javaModelGenerator> | 实体类生成器 | 是 |
| <sqlMapGenerator> | XML 生成器 | XMLMAPPER 模式必填 |
| <javaClientGenerator> | Mapper 接口生成器 | 是 |
| <table> | 表配置 | 是 |
MBG 内置插件速查表
| 插件 | 功能 |
|---|---|
| SerializablePlugin | 实体类实现 Serializable |
| ToStringPlugin | 生成 toString() |
| EqualsAndHashCodePlugin | 生成 equals() 和 hashCode() |
| FluentBuilderMethodsPlugin | 链式 setter |
| RowBoundsPlugin | RowBounds 分页 |
| RenameExampleClassPlugin | 重命名 Example 类 |
| MapperNamePlugin | 自定义 Mapper 名称 |
| UnmergeableXmlMapperPlugin | XML 强制覆盖 |
| CaseInsensitiveLikePlugin | 大小写不敏感 LIKE |
| VirtualKeyPlugin | 虚拟主键 |
MBG targetRuntime 速查表
| 值 | 生成 Example | 生成 XML | 说明 |
|---|---|---|---|
| MyBatis3 | ✅ | ✅ | 完整版(默认) |
| MyBatis3Simple | ❌ | ✅ | 简化版(仅基本 CRUD) |
| MyBatis3DynamicSql | ❌ | ❌ | 动态 SQL DSL 版 |
| MyBatis3Kotlin | ❌ | ❌ | Kotlin 版 |
MP Generator 配置速查表
| 配置项 | 方法 | 说明 |
|---|---|---|
| 作者 | globalConfig.author(“xxx”) | 注释中的作者名 |
| 输出目录 | globalConfig.outputDir(“path”) | 代码输出根目录 |
| Swagger | globalConfig.enableSwagger() | 开启 Swagger 注解 |
| 父包 | packageConfig.parent(“com.xxx”) | 根包名 |
| 表名 | strategyConfig.addInclude(“table”) | 指定要生成的表 |
| 表前缀 | strategyConfig.addTablePrefix(“t_“) | 去掉表前缀 |
| Lombok | entityBuilder.enableLombok() | 使用 Lombok |
| 主键策略 | entityBuilder.idType(IdType.AUTO) | 主键生成策略 |
| 命名策略 | entityBuilder.naming(…) | 下划线转驼峰 |
| RestController | controllerBuilder.enableRestStyle() | REST 风格 |
Example 方法速查表
EmpExample example = new EmpExample();
// 创建 AND 条件组
EmpExample.Criteria criteria = example.createCriteria();
// 等值
criteria.andXxxEqualTo(value); // = value
criteria.andXxxNotEqualTo(value); // <> value
// 范围
criteria.andXxxGreaterThan(value); // > value
criteria.andXxxGreaterThanOrEqualTo(value); // >= value
criteria.andXxxLessThan(value); // < value
criteria.andXxxLessThanOrEqualTo(value); // <= value
criteria.andXxxBetween(v1, v2); // BETWEEN v1 AND v2
criteria.andXxxNotBetween(v1, v2); // NOT BETWEEN
// 集合
criteria.andXxxIn(list); // IN (...)
criteria.andXxxNotIn(list); // NOT IN (...)
// 模糊
criteria.andXxxLike("%keyword%"); // LIKE '%keyword%'
criteria.andXxxNotLike("%keyword%"); // NOT LIKE '%keyword%'
// NULL
criteria.andXxxIsNull(); // IS NULL
criteria.andXxxIsNotNull(); // IS NOT NULL
// OR 条件组
example.or().andXxxEqualTo(value); // OR (...)
// 排序
example.setOrderByClause("salary DESC");
// 去重
example.setDistinct(true);类型映射速查表
| MySQL 类型 | Java 类型(useJSR310Types=true) | JDBC 类型 |
|---|---|---|
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 |
常用命令速查表
# ====== MBG ======
# Maven 插件执行
mvn mybatis-generator:generate
# 指定配置文件
mvn mybatis-generator:generate -DconfigurationFile=src/main/resources/generatorConfig.xml
# 不覆盖已有文件
mvn mybatis-generator:generate -Doverwrite=false
# 命令行执行
java -jar mybatis-generator-core-1.4.2.jar -configfile generatorConfig.xml -overwrite
# ====== MP Generator ======
# 直接运行 main 方法(IDE 中右键运行)
# 或使用 Maven exec 插件
mvn exec:java -Dexec.mainClass="com.example.generator.CodeGenerator"总结: MyBatis 逆向工程的核心在于理解「数据库表结构 → Java 代码」的自动映射过程。MyBatis Generator 是官方工具,稳定可靠,适合生成基础 DAO 层代码;MyBatis-Plus Generator 功能更丰富,能一键生成从 Entity 到 Controller 的全栈代码。两者都可以通过自定义插件和模板深度定制生成结果。掌握逆向工程可以大幅提升开发效率,但需谨记:应将可重复生成的代码与自定义代码分离,并在重新生成前确认覆盖范围;扩展查询可放在独立 Mapper、扩展接口或自定义 SQL 中。
喜欢的话,留下你的评论吧~