以下是添加了详细中文注释说明的 mybatis-plus-config.java.ftl 模板文件,涵盖每一行代码的设计意图、配置原理和企业级最佳实践:
package ${package.Config};
<#-- 导入 MyBatis-Plus 核心插件和拦截器 -->
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
<#-- 导入 MyBatis 原生 MetaObject,用于自动填充 -->
import org.apache.ibatis.reflection.MetaObject;
<#-- Spring 注解 -->
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
/**
* <p>
* MyBatis-Plus 全局配置类
* </p>
*
* <p>
* 【核心作用】
* 1. 注册 MyBatis-Plus 插件(如分页插件)
* 2. 配置自动填充处理器(Auto Fill Handler)
* 3. 统一数据库方言、逻辑删除等全局行为
* </p>
*
* <p>
* 【设计原则】
* - 通过 Spring {@code @Configuration} 和 {@code @Bean} 声明式配置
* - 插件链式注册,便于扩展(如添加多租户、性能分析插件)
* - 自动填充逻辑集中管理,避免在每个 Entity 中重复
* - 与 application.yml 配置互补,代码配置优先级更高
* </p>
*
* <p>
* 【注意事项】
* - 此配置类会被 Spring 自动扫描并加载(需在 ComponentScan 范围内)
* - 若同时存在 application.yml 中的 MP 配置,代码配置会覆盖 YAML 配置
* - 分页插件必须在第一个位置(MyBatis-Plus 要求)
* </p>
*
* @author ${author}
* @since ${date}
*/
@Configuration
public class MybatisPlusConfig {
/**
* 注册 MyBatis-Plus 拦截器链
*
* <p>
* 【拦截器链说明】
* MyBatis-Plus 通过 {@link MybatisPlusInterceptor} 管理多个插件,
* 插件按添加顺序执行(注意:分页插件必须放在第一位!)。
* </p>
*
* <p>
* 【当前注册的插件】
* - {@link PaginationInnerInterceptor}:分页插件
* - 自动拦截 {@code selectPage} 等方法
* - 改写 SQL 添加 LIMIT/OFFSET
* - 支持多种数据库方言(MySQL、Oracle、PostgreSQL 等)
* </p>
*
* <p>
* 【扩展建议】
* 可在此添加更多插件,例如:
* - {@code BlockAttackInnerInterceptor}:防止全表更新/删除
* - {@code TenantLineInnerInterceptor}:多租户插件
* - {@code OptimisticLockerInnerInterceptor}:乐观锁插件
* </p>
*
* @return 配置好的 MyBatis-Plus 拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 创建拦截器实例
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(必须放在第一位!)
// DbType.MYSQL 表示使用 MySQL 方言(自动处理 LIMIT 语法)
// 其他常用值:DbType.ORACLE, DbType.POSTGRE_SQL, DbType.SQL_SERVER
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 【扩展点】在此添加其他插件
// interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
/**
* 注册自动填充处理器
*
* <p>
* 【自动填充原理】
* 当执行 INSERT/UPDATE 操作时,MyBatis-Plus 会回调此处理器,
* 根据字段上的 {@code @TableField(fill = ...)} 注解自动填充值。
* </p>
*
* <p>
* 【填充策略说明】
* - {@code FieldFill.INSERT}:仅插入时填充
* - {@code FieldFill.UPDATE}:仅更新时填充
* - {@code FieldFill.INSERT_UPDATE}:插入和更新时都填充
* </p>
*
* <p>
* 【当前填充逻辑】
* 1. 插入时:
* - createTime = 当前时间
* - updateTime = 当前时间
* - deleted = 0(未删除)
* 2. 更新时:
* - updateTime = 当前时间
* </p>
*
* <p>
* 【关键方法说明】
* - {@code strictInsertFill}:严格插入填充(字段必须存在)
* - {@code strictUpdateFill}:严格更新填充(字段必须存在)
* - 使用 Lambda 表达式提供值(如 {@code LocalDateTime::now})
* </p>
*
* <p>
* 【与 Entity 的配合】
* Entity 字段需添加注解,例如:
* {@code @TableField(fill = FieldFill.INSERT)}
* private LocalDateTime createTime;
* </p>
*
* @return 自动填充处理器实例
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
/**
* 插入操作自动填充
*
* <p>
* 【执行时机】
* 在执行 {@code insert} 或 {@code insertBatch} 前触发
* </p>
*
* <p>
* 【填充字段】
* - createTime:创建时间(仅插入时设置)
* - updateTime:更新时间(插入时也设置为当前时间)
* - deleted:逻辑删除标识(默认 0 表示未删除)
* </p>
*
* @param metaObject MyBatis 元对象,包含实体信息
*/
@Override
public void insertFill(MetaObject metaObject) {
// 填充 createTime 字段(仅当字段存在且未手动设置时)
// LocalDateTime::now 是 Supplier<LocalDateTime>,提供当前时间
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
// this.strictInsertFill(metaObject, BaseEntity::getCreateTime, LocalDateTime::now, LocalDateTime.class);
// 填充 updateTime 字段(插入时也设为当前时间)
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
// this.strictInsertFill(metaObject, BaseEntity::getUpdateTime, LocalDateTime::now, LocalDateTime.class);
// 填充 deleted 字段(默认 0 表示未删除)
// 使用 Lambda 表达式提供默认值:() -> 0
this.strictInsertFill(metaObject, "deleted", () -> 0, Integer.class);
// this.strictInsertFill(metaObject, BaseEntity::getDeleted, () -> 0, Integer.class);
}
/**
* 更新操作自动填充
*
* <p>
* 【执行时机】
* 在执行 {@code updateById} 或 {@code update} 前触发
* </p>
*
* <p>
* 【填充字段】
* - updateTime:每次更新时刷新为当前时间
* </p>
*
* @param metaObject MyBatis 元对象,包含实体信息
*/
@Override
public void updateFill(MetaObject metaObject) {
// 仅填充 updateTime 字段
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
// this.strictInsertFill(metaObject, BaseEntity::getUpdateTime, LocalDateTime::now, LocalDateTime.class);
}
};
}
}
🔍 关键设计说明补充
1. 为什么分页插件必须放在第一位?
- MyBatis-Plus 内部对插件执行顺序有严格要求
- 分页插件需要最先解析 SQL,才能正确提取原始查询并改写
- 若放在后面,可能被其他插件(如多租户)干扰,导致分页失效
2. strictInsertFill vs fillStrategy
strictInsertFill:严格模式,仅当字段未设置值时才填充- 避免覆盖用户手动设置的值(如创建时指定特殊时间)
- 推荐始终使用
strict*方法,而非已废弃的fillStrategy
3. 数据库方言(DbType)选择
| 数据库 | DbType 常量 | 说明 |
|---|---|---|
| MySQL | DbType.MYSQL | 默认使用 |
| PostgreSQL | DbType.POSTGRE_SQL | 注意拼写 |
| Oracle | DbType.ORACLE | 需额外配置 |
| SQL Server | DbType.SQL_SERVER | - |
| 达梦 | DbType.DM | 国产数据库 |
4. 自动填充的替代方案
-
方案A(推荐):代码配置(本模板方式)— 灵活、类型安全
-
方案B:YAML 配置 — 简单但不够灵活
mybatis-plus: global-config: db-config: insert-strategy: not_null update-strategy: not_null
5. 生产环境增强建议
// 防止全表更新/删除(安全防护)
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
// 乐观锁插件(需 Entity 添加 @Version 注解)
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
✅ 此模板文件是 MyBatis-Plus 应用的核心配置,正确配置后可:
- 实现无侵入式分页
- 自动管理创建/更新时间
- 统一逻辑删除行为
- 为后续扩展(多租户、审计等)奠定基础
可直接用于企业级项目,确保数据访问层的健壮性和可维护性。
749

被折叠的 条评论
为什么被折叠?



