SpringBoot 裝飾器@TableField原理解析
1. 開胃菜:一句話認(rèn)識 @TableField
@TableField 是 MyBatis-Plus 提供的字段級注解,用于非主鍵字段與數(shù)據(jù)庫列的顯式綁定與行為控制。
記住兩個(gè)關(guān)鍵字:“非主鍵”、“行為控制”——這是它與 @TableId 的核心差異。
2. 為什么需要它?
| 場景 | 不用 @TableField 的問題 | 用了 @TableField 的收益 |
|---|---|---|
| 實(shí)體字段名與列名不一致 | 默認(rèn)下劃線轉(zhuǎn)駝峰失效 | 顯式指定 value="user_name" |
| 實(shí)體屬性非數(shù)據(jù)庫字段 | MP 會無腦拼接,SQL 報(bào)錯(cuò) | exist=false 排除 |
| 敏感字段不允許查詢 | 查詢會返回密碼 | select=false 自動屏蔽 |
| 插入/更新時(shí)要填充 | 手動 set 易遺漏 | fill=FieldFill.INSERT 自動填充 |
| 樂觀鎖版本號 | 需要手動判斷 | version=true 一鍵啟用 |
3. 快速上手:5 個(gè)最常用的屬性
@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. 查詢時(shí)不返回
@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 → 反射讀取所有字段 → 構(gòu)建 TableFieldInfo 列表。
4.2 SQL 拼接
AbstractSqlInjector 注入通用 CRUD 方法 →DefaultSqlProvider 按 TableFieldInfo 生成 SQL 片段 →select=false 的字段被主動剔除,exist=false 的字段直接忽略。
4.3 填充與樂觀鎖
MetaObjectHandler 接口處理 FieldFill;MybatisPlusInterceptor 內(nèi)的 OptimisticLockerInnerInterceptor 處理 @Version。
5. 企業(yè)級高階玩法
5.1 動態(tài)過濾字段 —— 防止數(shù)據(jù)越權(quán)
// 場景:普通用戶不能查看薪資字段 @TableField(select = false, condition = "%s != 'admin'") private BigDecimal salary;
配合 TenantLineInterceptor 可實(shí)現(xiàn)動態(tài)列級權(quán)限。
5.2 加密存儲 —— 自定義 TypeHandler
@TableField(typeHandler = AesTypeHandler.class) private String idCard;
插入自動加密,查詢自動解密,業(yè)務(wù)代碼零侵入。
5.3 多租戶隔離 —— 租戶字段加索引
@TableField("tenant_id")
@InterceptorIgnore(tenantLine = "true") // 跳過插件,避免死循環(huán)
private Long tenantId;6. 高頻踩坑 & 排查清單
| 現(xiàn)象 | 根因 | 正確姿勢 |
|---|---|---|
| 更新時(shí) version 不生效 | 實(shí)體里 version 為 null | 先查后改,或者手動 set |
| 虛擬字段被持久化 | 忘記寫 exist=false | 加上即可 |
| select=false 依然查出 | 手寫 SQL 未使用 MP 條件構(gòu)造器 | 用 LambdaQueryWrapper 或 SqlSelectInterceptor |
| 填充不生效 | 沒實(shí)現(xiàn) MetaObjectHandler | 加 @Component 實(shí)現(xiàn)接口 |
7. 和 Spring 注解的“夢幻聯(lián)動”
@Component
public class MyMetaHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// Spring 容器直接注入
this.strictInsertFill(metaObject, "createTime",
LocalDateTime.class, LocalDateTime.now());
}
}@TableField 負(fù)責(zé)標(biāo)記,Spring 負(fù)責(zé)注入,兩者解耦,符合單一職責(zé)。
8. 總結(jié):一張腦圖記住 @TableField
@TableField ├─ value 映射列名 ├─ exist 是否持久化 ├─ select 是否參與查詢 ├─ fill 自動填充策略 ├─ update 自定義 set 片段 ├─ condition 動態(tài)條件 ├─ typeHandler 類型轉(zhuǎn)換 └─ keepGlobalFormat 保留全局格式
9. 一鍵復(fù)制即可跑的最小 Demo
GitHub:https://github.com/YOUR_NAME/mp-tablefield-demo
分支:feature/tablefield-demo
包含:加密、樂觀鎖、多租戶、自動填充 4 大示例,Maven 一鍵啟動。
10. 評論區(qū) FAQ 預(yù)覽(提前回答)
Q1:@TableField 和 @Column 有什么區(qū)別?
A:后者是 JPA 規(guī)范,只在 Hibernate 生效;前者是 MyBatis-Plus 專用,功能更強(qiáng)。
Q2:能否在 Kotlin data class 使用?
A:可以,但字段必須聲明為 var,否則無法填充。
Q3:性能會下降嗎?
A:解析結(jié)果緩存到 TableInfo,運(yùn)行期零反射,性能損耗 < 1%。
到此這篇關(guān)于一文講透一個(gè) SpringBoot 裝飾器 —— @TableField的文章就介紹到這了,更多相關(guān)SpringBoot 裝飾器@TableField內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的遠(yuǎn)程方法調(diào)用之RPC使用
本文介紹了RPC的基本概念、核心思想及其在分布式系統(tǒng)中的作用,重點(diǎn)講解了Java中的三種RPC實(shí)現(xiàn)方式:RMI、Hessian和Dubbo,分別從適用場景、核心特點(diǎn)等方面做了對比對比較,并并提供了其基本實(shí)現(xiàn)代碼示例,總結(jié)了各框架的優(yōu)缺點(diǎn),為實(shí)際開發(fā)中選擇合適的技術(shù)棧提供了參考2026-05-05
IDEA設(shè)置Maven自動編譯model的實(shí)現(xiàn)方法
本文主要介紹了IDEA設(shè)置Maven自動編譯model的實(shí)現(xiàn)方法, maven結(jié)構(gòu)的項(xiàng)目,我們在每次修改代碼后都會需要手動編譯,本文就可以解決這個(gè)問題,感興趣的可以了解一下2023-08-08
Java數(shù)據(jù)結(jié)構(gòu)中的HashMap和HashSet詳解
HashMap和HashSet都是存儲在哈希桶之中,通過本文我們可以先了解一些哈希桶是什么,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-10-10

