1. MyBatis逆向工程概述
在Java企业级开发中,数据持久层的工作量往往占到整个项目的30%-40%。传统的手写SQL和实体类不仅效率低下,而且容易出错。MyBatis逆向工程(MyBatis Generator)正是为解决这个问题而生的利器,它能够根据数据库表结构自动生成实体类、Mapper接口和XML映射文件。
我第一次接触逆向工程是在2016年一个电商后台项目中,当时需要处理58张表的CRUD操作。手动编写这些基础代码花费了团队近两周时间,而后来采用逆向工程后,同样的工作仅需2小时就能完成,且代码风格统一、质量可靠。
2. 环境准备与工具选型
2.1 基础环境配置
推荐使用以下环境组合:
- JDK 1.8+(兼容性最佳)
- Maven 3.6+(依赖管理)
- Spring Boot 2.5.x(整合基础)
- MyBatis 3.5.7+(核心框架)
- MyBatis Generator 1.4.1+(逆向工程核心)
注意:MyBatis Generator与MyBatis主版本需要保持兼容,新项目建议使用最新稳定版组合。
2.2 插件选择建议
除了官方提供的核心插件外,这些增强插件值得关注:
- Lombok插件:自动生成getter/setter(需在实体类上添加@Getter/@Setter注解)
- Swagger插件:自动生成API文档注解
- Comment插件:生成更完善的字段注释
- Serializable插件:实现Serializable接口
xml复制<!-- 典型插件配置示例 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
3. Spring整合完整配置
3.1 Maven插件配置
在pom.xml中添加如下配置:
xml复制<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
3.2 generatorConfig.xml详解
核心配置文件需要包含以下部分:
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>
<!-- 数据库连接配置 -->
<context id="mysql" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/your_db?useSSL=false"
userId="root"
password="123456">
</jdbcConnection>
<!-- 实体类生成配置 -->
<javaModelGenerator targetPackage="com.example.entity"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- XML映射文件配置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapper接口配置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 表配置示例 -->
<table tableName="user" domainObjectName="User"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
4. 高级定制技巧
4.1 自定义类型处理器
当需要处理特殊字段类型时(如JSON、枚举等),可以通过TypeHandler实现:
java复制public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Map<String, Object> parameter, JdbcType jdbcType) {
ps.setString(i, JSON.toJSONString(parameter));
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) {
return JSON.parseObject(rs.getString(columnName));
}
// 其他重写方法...
}
在配置文件中添加:
xml复制<table tableName="config">
<columnOverride column="config_data"
javaType="java.util.Map"
typeHandler="com.example.handler.JsonTypeHandler"/>
</table>
4.2 动态表名处理
在分表场景下,可以通过继承Example类实现:
java复制public class DynamicTableExample extends Example {
private String tableSuffix;
public DynamicTableExample(Class<?> entityClass, String tableSuffix) {
super(entityClass);
this.tableSuffix = tableSuffix;
}
@Override
public String getTableName() {
return super.getTableName() + "_" + tableSuffix;
}
}
5. 常见问题解决方案
5.1 中文注释乱码问题
解决方法:
- 在generatorConfig.xml中添加:
xml复制<commentGenerator>
<property name="suppressAllComments" value="false"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
- 确保数据库连接URL包含characterEncoding参数:
code复制jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8
5.2 字段名与属性名映射异常
典型场景:数据库使用下划线命名(user_name),Java使用驼峰命名(userName)
解决方案:
- 全局配置:
xml复制<context id="mysql" targetRuntime="MyBatis3">
<property name="useActualColumnNames" value="false"/>
...
</context>
- 单个字段覆盖:
xml复制<table tableName="user">
<columnOverride column="user_name" property="userName"/>
</table>
6. 最佳实践建议
-
版本控制策略:
- 生成的实体类建议纳入版本控制
- XML映射文件建议不纳入版本控制(通过.gitignore过滤)
- 每次数据库变更后重新生成代码
-
生成代码二次开发:
- 实体类继承基类实现公共字段
- 使用@MapperScan批量扫描接口
- 自定义BaseMapper提供通用方法
-
性能优化:
- 批量操作使用BatchExecutor
- 复杂查询使用@SelectProvider
- 关联查询使用
嵌套
-
监控与调试:
- 开启MyBatis日志:logging.level.org.mybatis=DEBUG
- 使用P6Spy监控SQL执行
- 集成Arthas进行运行时诊断
7. Spring Boot深度整合
7.1 自动化配置方案
创建自动配置类:
java复制@Configuration
@ConditionalOnClass(SqlSessionFactory.class)
public class MybatisGeneratorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MybatisGeneratorRunner mybatisGeneratorRunner() {
return new MybatisGeneratorRunner();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
return factory.getObject();
}
}
7.2 多数据源支持
配置多个生成器实例:
java复制@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public PlatformTransactionManager primaryTxManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
8. 实际案例演示
以电商系统的商品模块为例:
- 数据库表设计:
sql复制CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`product_name` varchar(100) NOT NULL,
`price` decimal(10,2) NOT NULL,
`stock` int NOT NULL DEFAULT '0',
`attributes` json DEFAULT NULL,
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 生成后的核心代码结构:
code复制src/main/java
└── com
└── example
├── entity
│ └── Product.java
└── mapper
└── ProductMapper.java
src/main/resources
└── mapper
└── ProductMapper.xml
- 典型业务使用示例:
java复制@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductMapper productMapper;
@Transactional
public void reduceStock(Long productId, int quantity) {
Product product = productMapper.selectByPrimaryKey(productId);
if (product.getStock() < quantity) {
throw new BusinessException("库存不足");
}
product.setStock(product.getStock() - quantity);
productMapper.updateByPrimaryKey(product);
}
public PageInfo<Product> queryProducts(ProductQuery query, Pageable pageable) {
Example example = new Example(Product.class);
example.createCriteria()
.andLike("productName", "%" + query.getKeyword() + "%")
.andBetween("price", query.getMinPrice(), query.getMaxPrice());
PageHelper.startPage(pageable.getPageNumber(), pageable.getPageSize());
List<Product> products = productMapper.selectByExample(example);
return new PageInfo<>(products);
}
}
9. 扩展与进阶
9.1 代码生成器增强
通过自定义CommentGenerator实现更丰富的注释:
java复制public class CustomCommentGenerator extends DefaultCommentGenerator {
@Override
public void addFieldComment(Field field,
IntrospectedTable table,
IntrospectedColumn column) {
field.addJavaDocLine("/**");
field.addJavaDocLine(" * " + column.getRemarks());
field.addJavaDocLine(" * 数据库字段: " + column.getActualColumnName());
field.addJavaDocLine(" */");
}
}
9.2 动态SQL扩展
在生成的Mapper接口中添加自定义方法:
java复制public interface ProductMapper extends BaseMapper<Product> {
@UpdateProvider(type = ProductSqlProvider.class, method = "updateSelective")
int updateSelective(@Param("record") Product record);
class ProductSqlProvider {
public String updateSelective(Product record) {
return new SQL() {{
UPDATE("product");
if (record.getProductName() != null) {
SET("product_name = #{record.productName}");
}
if (record.getPrice() != null) {
SET("price = #{record.price}");
}
WHERE("id = #{record.id}");
}}.toString();
}
}
}
9.3 多模块工程整合
在微服务架构下的配置方案:
- 创建generator模块专门负责代码生成
- 配置依赖关系:
xml复制<!-- generator模块pom.xml -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
- 生成的代码输出到指定模块:
xml复制<javaModelGenerator targetPackage="com.example.product.entity"
targetProject="../product-service/src/main/java"/>
10. 性能优化实践
10.1 批量操作优化
java复制public void batchInsert(List<Product> products) {
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
ProductMapper mapper = session.getMapper(ProductMapper.class);
for (Product product : products) {
mapper.insert(product);
}
session.commit();
} finally {
session.close();
}
}
10.2 二级缓存配置
在Mapper.xml中添加:
xml复制<cache eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
Spring Boot中启用缓存:
java复制@MapperScan("com.example.mapper")
@EnableCaching
public class MybatisConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("productCache");
}
}
11. 监控与诊断
11.1 SQL执行监控
配置P6Spy:
properties复制# application.properties
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/db
p6spy配置:
properties复制# spy.properties
module.log=com.p6spy.engine.logging.P6LogFactory
filter=true
exclude=QRTZ_
11.2 慢SQL统计
使用MyBatis插件:
java复制@Intercepts(@Signature(type= StatementHandler.class,
method="query",
args={Statement.class, ResultHandler.class}))
public class SlowSqlInterceptor implements Interceptor {
private static final long SLOW_THRESHOLD = 1000;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long cost = System.currentTimeMillis() - start;
if (cost > SLOW_THRESHOLD) {
StatementHandler handler = (StatementHandler) invocation.getTarget();
String sql = handler.getBoundSql().getSql();
log.warn("Slow SQL detected: {}ms - {}", cost, sql);
}
return result;
}
}
12. 安全注意事项
-
SQL注入防护:
- 始终使用#{}占位符
- 避免直接拼接SQL语句
- 对Like查询使用CONCAT函数:
java复制example.createCriteria() .andLike("name", "%" + keyword + "%");
-
敏感数据处理:
- 密码字段使用TypeHandler加密
- 审计日志记录数据变更
- 实现字段级别的权限控制
-
连接池安全:
properties复制spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.idle-timeout=600000
13. 现代化演进方案
13.1 与JPA混合使用
java复制@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name")
private String username;
@Transient
private List<Role> roles;
}
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.username like %:keyword%")
Page<User> search(@Param("keyword") String keyword, Pageable pageable);
@Query(value = "SELECT * FROM user WHERE status = :status", nativeQuery = true)
List<User> findByStatus(@Param("status") Integer status);
}
13.2 Kotlin支持方案
生成Kotlin实体类配置:
xml复制<javaModelGenerator targetPackage="com.example.entity"
targetProject="src/main/kotlin"
type="KOTLIN">
<property name="useActualColumnNames" value="false"/>
</javaModelGenerator>
生成的Kotlin实体类示例:
kotlin复制data class User(
@Column(name = "id")
var id: Long? = null,
@Column(name = "user_name")
var username: String? = null
)
14. 持续集成方案
14.1 Maven自动化生成
在pom.xml中添加执行配置:
xml复制<executions>
<execution>
<id>generate-mybatis</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
14.2 Jenkins集成
Jenkinsfile配置示例:
groovy复制pipeline {
agent any
stages {
stage('Generate Code') {
steps {
sh 'mvn mybatis-generator:generate'
}
}
}
}
15. 替代方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| MyBatis逆向工程 | 生成代码规范,与MyBatis无缝集成 | 复杂查询仍需手动编写 | 传统CRUD为主的系统 |
| JPA/Hibernate | 标准规范,开发效率高 | 复杂SQL性能较差 | 快速开发的中小型项目 |
| QueryDSL | 类型安全,编译时检查 | 学习成本较高 | 需要强类型检查的项目 |
| MyBatis-Plus | 功能丰富,开箱即用 | 自定义程度受限 | 需要快速开发的CRUD系统 |
| jOOQ | 纯Java SQL构建,功能强大 | 商业版收费,学习曲线陡峭 | 复杂SQL场景 |
16. 未来演进方向
-
云原生适配:
- 支持Kubernetes配置中心
- 集成服务网格观测能力
- 适应Serverless架构
-
智能代码生成:
- 基于AI的代码建议
- 自动异常处理生成
- 智能索引推荐
-
多语言支持:
- 生成TypeScript类型定义
- 支持GraphQL Schema生成
- 生成Protobuf消息定义
-
DevOps集成:
- 生成OpenAPI文档
- 自动生成测试用例
- 生成部署描述文件
17. 个人实践心得
在实际项目中使用MyBatis逆向工程多年,总结出这些经验:
-
版本控制策略:生成的实体类建议纳入版本控制,但XML文件可以通过每次构建重新生成。这样既保证了核心模型的稳定性,又避免了映射文件的冲突。
-
定制化开发:不要直接修改生成的代码,而是通过继承或组合扩展功能。例如创建BaseEntity包含通用字段,让生成的实体类继承它。
-
生成时机控制:在Maven的generate-sources阶段自动执行生成,但配置overwrite=false避免意外覆盖。重大变更时再手动启用覆盖。
-
多环境适配:通过Maven Profile区分开发、测试、生产环境的数据库连接配置,避免敏感信息泄露。
-
文档补充:虽然生成的代码自带基础注释,但复杂业务逻辑仍需补充详细的JavaDoc。我们团队要求每个领域模型都必须有业务含义说明。
-
性能监控:为生成的Mapper方法添加@LogExecutionTime注解,监控SQL执行效率,及时发现性能问题。
-
代码审查:虽然生成的代码质量较高,但仍需纳入常规代码审查流程,特别关注动态SQL的安全性和效率。