一、核心概念
@TableField
注解用于标注在实体类的字段上,主要作用是定义该字段与数据库表列的映射关系,并控制该字段在 CRUD 操作中的行为。
1. 基本作用
- 字段映射:指定 Java 字段对应的数据库列名(当两者不一致时)。
- 逻辑控制:控制字段是否参与插入、更新、查询等操作。
- 自动填充:支持创建时间、更新时间等字段的自动填充。
- 条件构造:配合查询条件构造器使用。
2. 适用场景
- Java 字段名与数据库列名不一致(如
userName
对应user_name
)。 - 某些字段不需要持久化(如 DTO 字段、计算字段)。
- 实现
create_time
、update_time
等字段的自动填充。 - 敏感字段(如密码)在查询时默认不返回。
二、操作步骤(非常详细)
步骤 1:引入 MyBatis-Plus 依赖
确保项目中已正确引入 MyBatis-Plus(以 Maven 为例):
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
步骤 2:创建实体类
import com.baomidou.mybatisplus.annotation.*;
import java.time.LocalDateTime;
@TableName("user") // 指定表名
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
// 其他字段...
}
步骤 3:使用 @TableField
映射字段
场景 1:字段名不一致
数据库列名为 user_name
,Java 字段为 userName
。
@TableField("user_name")
private String userName;
说明:
@TableField("列名")
明确指定映射关系。
场景 2:字段不参与数据库操作(如 DTO 字段)
@TableField(exist = false)
private String fullName; // 不是数据库字段,仅用于传输
说明:
exist = false
表示该字段不对应数据库中的任何列。
场景 3:插入或更新时忽略该字段
@TableField(insertStrategy = FieldStrategy.NOT_NULL, updateStrategy = FieldStrategy.NOT_NULL)
private String remark;
说明:策略控制字段在插入/更新时的行为(见“使用技巧”部分)。
场景 4:实现自动填充(如创建/更新时间)
- 在字段上使用
fill
属性:
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
- 实现
MetaObjectHandler
接口:
@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());
}
}
说明:
strictInsertFill
和strictUpdateFill
是推荐的填充方式,避免 NPE。
三、常见错误
错误 | 原因 | 解决方案 |
---|---|---|
Unknown column 'xxx' in 'field list' |
字段名未正确映射或 exist = false 忘记添加 |
使用 @TableField("列名") 或 @TableField(exist = false) |
自动填充未生效 | 未实现 MetaObjectHandler 或 fill 属性未设置 |
确保 fill 属性正确,且 MetaObjectHandler 已注册为 Spring Bean |
插入时字段为 NULL 仍被插入 | 未设置 insertStrategy |
使用 FieldStrategy.NOT_NULL 或 IGNORED |
@TableField 写在 @TableId 字段上 |
@TableId 字段不应再使用 @TableField |
移除 @TableField ,仅保留 @TableId |
四、注意事项
@TableId
与@TableField
互斥:主键字段使用@TableId
,不要同时使用@TableField
。- 驼峰转下划线:MyBatis-Plus 默认开启驼峰命名转换(
map-underscore-to-camel-case: true
),若关闭则需显式使用@TableField
。 exist = false
的字段:不会参与任何 SQL 拼接,适合 DTO、VO 中的非持久化字段。- 自动填充字段:必须在
MetaObjectHandler
中处理,否则不会自动填充。 - 版本兼容性:
FieldStrategy
在 3.3.0+ 版本中已标记为过时,推荐使用@TableField(fill = ...)
+MetaObjectHandler
。
五、使用技巧
1. 策略控制(strategy
属性)
FieldStrategy.DEFAULT
:默认策略,根据全局配置。FieldStrategy.NOT_NULL
:非 NULL 时才参与 SQL。FieldStrategy.IGNORED
:始终参与 SQL(即使为 NULL)。FieldStrategy.NOT_EMPTY
:非空字符串或非 NULL 时参与。
推荐:使用
NOT_NULL
避免插入 NULL 值。
2. 条件构造器中使用
// 查询时排除某些字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "user_name"); // 只查 id 和 user_name
3. el
属性(旧版本)
早期版本支持 el
属性进行映射(如 @TableField("user_name, jdbcType=VARCHAR")
),现已不推荐。
六、最佳实践与性能优化
最佳实践
- 显式映射:即使字段名一致,也建议使用
@TableField
明确声明,提高可读性。 - 自动填充:所有
create_time
、update_time
必须使用自动填充,避免业务代码中手动赋值。 - 敏感字段:密码等字段使用
@TableField(select = false)
,查询时默认不返回。 - DTO 分离:实体类只包含持久化字段,DTO 用于接口传输,避免
exist = false
滥用。
性能优化
- 避免
SELECT *
:使用QueryWrapper.select()
指定需要的字段,减少网络传输。 - 批量操作:结合
@TableField(fill = ...)
,在批量插入时自动填充时间字段,减少循环赋值。 - 缓存策略:对于频繁查询但不常更新的字段,结合 MyBatis 一级/二级缓存优化性能。
总结
@TableField
是 MyBatis-Plus 中灵活控制字段映射和行为的核心注解。