SpringBoot 装饰器@TableField原理解析
作者:司南锤
1. 开胃菜:一句话认识 @TableField
@TableField
是 MyBatis-Plus 提供的字段级注解,用于非主键字段与数据库列的显式绑定与行为控制。
记住两个关键字:“非主键”、“行为控制”——这是它与 @TableId
的核心差异。
2. 为什么需要它?
场景 | 不用 @TableField 的问题 | 用了 @TableField 的收益 |
---|---|---|
实体字段名与列名不一致 | 默认下划线转驼峰失效 | 显式指定 value="user_name" |
实体属性非数据库字段 | MP 会无脑拼接,SQL 报错 | exist=false 排除 |
敏感字段不允许查询 | 查询会返回密码 | select=false 自动屏蔽 |
插入/更新时要填充 | 手动 set 易遗漏 | fill=FieldFill.INSERT 自动填充 |
乐观锁版本号 | 需要手动判断 | version=true 一键启用 |
3. 快速上手:5 个最常用的属性
@Data @TableName("t_user") public class User { @TableId(type = IdType.AUTO) private Long id; // 1. 列名不一致 @TableField("user_name") private String name; // 2. 虚拟字段,不持久化 @TableField(exist = false) private String token; // 3. 查询时不返回 @TableField(select = false) private String password; // 4. 插入自动写入 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; // 5. 更新自动写入 @TableField(fill = FieldFill.UPDATE) private LocalDateTime updateTime; // 6. 乐观锁 @Version @TableField("version") private Integer version; }
4. 源码级剖析:MP 如何把注解变成 SQL
4.1 解析入口
TableInfoHelper#initTableInfo
→ 反射读取所有字段 → 构建 TableFieldInfo
列表。
4.2 SQL 拼接
AbstractSqlInjector
注入通用 CRUD 方法 →DefaultSqlProvider
按 TableFieldInfo
生成 SQL 片段 →select=false
的字段被主动剔除,exist=false
的字段直接忽略。
4.3 填充与乐观锁
MetaObjectHandler
接口处理 FieldFill
;MybatisPlusInterceptor
内的 OptimisticLockerInnerInterceptor
处理 @Version
。
5. 企业级高阶玩法
5.1 动态过滤字段 —— 防止数据越权
// 场景:普通用户不能查看薪资字段 @TableField(select = false, condition = "%s != 'admin'") private BigDecimal salary;
配合 TenantLineInterceptor
可实现动态列级权限。
5.2 加密存储 —— 自定义 TypeHandler
@TableField(typeHandler = AesTypeHandler.class) private String idCard;
插入自动加密,查询自动解密,业务代码零侵入。
5.3 多租户隔离 —— 租户字段加索引
@TableField("tenant_id") @InterceptorIgnore(tenantLine = "true") // 跳过插件,避免死循环 private Long tenantId;
6. 高频踩坑 & 排查清单
现象 | 根因 | 正确姿势 |
---|---|---|
更新时 version 不生效 | 实体里 version 为 null | 先查后改,或者手动 set |
虚拟字段被持久化 | 忘记写 exist=false | 加上即可 |
select=false 依然查出 | 手写 SQL 未使用 MP 条件构造器 | 用 LambdaQueryWrapper 或 SqlSelectInterceptor |
填充不生效 | 没实现 MetaObjectHandler | 加 @Component 实现接口 |
7. 和 Spring 注解的“梦幻联动”
@Component public class MyMetaHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { // Spring 容器直接注入 this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); } }
@TableField
负责标记,Spring 负责注入,两者解耦,符合单一职责。
8. 总结:一张脑图记住 @TableField
@TableField ├─ value 映射列名 ├─ exist 是否持久化 ├─ select 是否参与查询 ├─ fill 自动填充策略 ├─ update 自定义 set 片段 ├─ condition 动态条件 ├─ typeHandler 类型转换 └─ keepGlobalFormat 保留全局格式
9. 一键复制即可跑的最小 Demo
GitHub:https://github.com/YOUR_NAME/mp-tablefield-demo
分支:feature/tablefield-demo
包含:加密、乐观锁、多租户、自动填充 4 大示例,Maven 一键启动。
10. 评论区 FAQ 预览(提前回答)
Q1:@TableField
和 @Column
有什么区别?
A:后者是 JPA 规范,只在 Hibernate 生效;前者是 MyBatis-Plus 专用,功能更强。
Q2:能否在 Kotlin data class 使用?
A:可以,但字段必须声明为 var
,否则无法填充。
Q3:性能会下降吗?
A:解析结果缓存到 TableInfo
,运行期零反射,性能损耗 < 1%。
到此这篇关于一文讲透一个 SpringBoot 装饰器 —— @TableField的文章就介绍到这了,更多相关SpringBoot 装饰器@TableField内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!