一、核心概念

@TableField 注解用于标注在实体类的字段上,主要作用是定义该字段与数据库表列的映射关系,并控制该字段在 CRUD 操作中的行为。

1. 基本作用

  • 字段映射:指定 Java 字段对应的数据库列名(当两者不一致时)。
  • 逻辑控制:控制字段是否参与插入、更新、查询等操作。
  • 自动填充:支持创建时间、更新时间等字段的自动填充。
  • 条件构造:配合查询条件构造器使用。

2. 适用场景

  • Java 字段名与数据库列名不一致(如 userName 对应 user_name)。
  • 某些字段不需要持久化(如 DTO 字段、计算字段)。
  • 实现 create_timeupdate_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:实现自动填充(如创建/更新时间)

  1. 在字段上使用 fill 属性
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
  1. 实现 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());
    }
}

说明strictInsertFillstrictUpdateFill 是推荐的填充方式,避免 NPE。


三、常见错误

错误 原因 解决方案
Unknown column 'xxx' in 'field list' 字段名未正确映射或 exist = false 忘记添加 使用 @TableField("列名")@TableField(exist = false)
自动填充未生效 未实现 MetaObjectHandlerfill 属性未设置 确保 fill 属性正确,且 MetaObjectHandler 已注册为 Spring Bean
插入时字段为 NULL 仍被插入 未设置 insertStrategy 使用 FieldStrategy.NOT_NULLIGNORED
@TableField 写在 @TableId 字段上 @TableId 字段不应再使用 @TableField 移除 @TableField,仅保留 @TableId

四、注意事项

  1. @TableId@TableField 互斥:主键字段使用 @TableId,不要同时使用 @TableField
  2. 驼峰转下划线:MyBatis-Plus 默认开启驼峰命名转换(map-underscore-to-camel-case: true),若关闭则需显式使用 @TableField
  3. exist = false 的字段:不会参与任何 SQL 拼接,适合 DTO、VO 中的非持久化字段。
  4. 自动填充字段:必须在 MetaObjectHandler 中处理,否则不会自动填充。
  5. 版本兼容性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")),现已不推荐。


六、最佳实践与性能优化

最佳实践

  1. 显式映射:即使字段名一致,也建议使用 @TableField 明确声明,提高可读性。
  2. 自动填充:所有 create_timeupdate_time 必须使用自动填充,避免业务代码中手动赋值。
  3. 敏感字段:密码等字段使用 @TableField(select = false),查询时默认不返回。
  4. DTO 分离:实体类只包含持久化字段,DTO 用于接口传输,避免 exist = false 滥用。

性能优化

  1. 避免 SELECT *:使用 QueryWrapper.select() 指定需要的字段,减少网络传输。
  2. 批量操作:结合 @TableField(fill = ...),在批量插入时自动填充时间字段,减少循环赋值。
  3. 缓存策略:对于频繁查询但不常更新的字段,结合 MyBatis 一级/二级缓存优化性能。

总结

@TableField 是 MyBatis-Plus 中灵活控制字段映射和行为的核心注解。