MyBatis-Plus字段自动填充:@TableField(fill = FieldFill.INSERT)详解

MyBatis-Plus字段自动填充:@TableField(fill = FieldFill.INSERT)详解

【免费下载链接】mybatis-plus An powerful enhanced toolkit of MyBatis for simplify development 【免费下载链接】mybatis-plus 项目地址: https://gitcode.com/gh_mirrors/my/mybatis-plus

痛点场景:为什么需要字段自动填充?

在日常开发中,我们经常需要处理一些公共字段的赋值问题,比如:

  • 创建时间:每条记录插入时都需要设置当前时间
  • 创建人:记录创建者的用户ID
  • 更新时间:记录修改时更新为当前时间
  • 版本号:乐观锁控制的版本字段

传统做法是在每个业务逻辑中手动设置这些值,但这会导致:

  1. 代码重复:相同的赋值逻辑散落在各个Service中
  2. 容易遗漏:开发人员可能忘记设置某些字段
  3. 维护困难:修改字段逻辑需要改动多处代码

MyBatis-Plus的字段自动填充功能正是为了解决这些问题而生!

FieldFill.INSERT 核心机制解析

字段填充策略枚举

MyBatis-Plus提供了四种字段填充策略:

public enum FieldFill {
    /** 默认不处理 */
    DEFAULT,
    /** 插入时填充字段 */
    INSERT,
    /** 更新时填充字段 */
    UPDATE,
    /** 插入和更新时填充字段 */
    INSERT_UPDATE
}

@TableField(fill = FieldFill.INSERT) 工作原理

当你在实体类字段上使用 @TableField(fill = FieldFill.INSERT) 注解时:

mermaid

实战:三步实现字段自动填充

第一步:实体类配置

在需要自动填充的字段上添加注解:

@Data
@TableName("user")
public class User {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    // 插入时自动填充创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    // 插入时自动填充创建人
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    
    // 插入和更新时都填充
    @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, "createBy", Long.class, getCurrentUserId());
        
        // 设置更新时间(INSERT_UPDATE策略也会触发)
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时设置更新时间
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    
    private Long getCurrentUserId() {
        // 实际项目中从SecurityContext或ThreadLocal获取
        return 1L; // 示例值
    }
}

第三步:配置启用(Spring Boot)

在配置类中启用自动填充:

@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
    
    @Bean
    public MyMetaObjectHandler myMetaObjectHandler() {
        return new MyMetaObjectHandler();
    }
}

高级用法与最佳实践

1. 严格模式填充

MyBatis-Plus 3.3.0+ 提供了严格模式填充,避免误填充:

@Override
public void insertFill(MetaObject metaObject) {
    // 严格模式:只有字段为null时才填充
    this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class);
    
    // 传统模式
    this.setFieldValByName("createBy", getCurrentUserId(), metaObject);
}

2. 多数据源场景

在多数据源环境下,需要为每个数据源配置独立的MetaObjectHandler:

@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public SqlSessionFactory primarySqlSessionFactory(
            @Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        
        // 设置自定义的MetaObjectHandler
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setMetaObjectHandler(new PrimaryMetaObjectHandler());
        
        factory.setConfiguration(configuration);
        return factory.getObject();
    }
}

3. 条件填充控制

可以通过重写openInsertFill和openUpdateFill方法控制填充开关:

@Component
public class ConditionalMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public boolean openInsertFill(MappedStatement mappedStatement) {
        // 根据MappedStatement ID判断是否开启插入填充
        String id = mappedStatement.getId();
        return !id.contains("batch"); // 批量操作不开启填充
    }
    
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充逻辑...
    }
    
    // updateFill方法...
}

常见问题与解决方案

Q1: 填充不生效怎么办?

排查步骤:

  1. 检查是否配置了MetaObjectHandler Bean
  2. 确认注解写法正确:@TableField(fill = FieldFill.INSERT)
  3. 查看字段名是否与实体属性名一致

Q2: 批量操作时填充异常?

解决方案:

@Override
public boolean openInsertFill(MappedStatement mappedStatement) {
    // 批量插入时关闭自动填充
    return !mappedStatement.getId().contains("Batch");
}

Q3: 如何填充复杂对象?

@Override
public void insertFill(MetaObject metaObject) {
    // 填充JSON对象
    UserInfo userInfo = new UserInfo(getCurrentUser());
    this.strictInsertFill(metaObject, "userInfo", UserInfo.class, userInfo);
}

性能优化建议

1. 避免频繁的反射操作

@Component
public class OptimizedMetaObjectHandler implements MetaObjectHandler {
    
    private final ThreadLocal<Boolean> fillEnabled = ThreadLocal.withInitial(() -> true);
    
    public void disableFill() {
        fillEnabled.set(false);
    }
    
    public void enableFill() {
        fillEnabled.set(true);
    }
    
    @Override
    public boolean openInsertFill(MappedStatement mappedStatement) {
        return fillEnabled.get();
    }
    
    // 其他方法...
}

2. 使用缓存减少重复计算

@Component
public class CachedMetaObjectHandler implements MetaObjectHandler {
    
    private final Cache<String, Object> valueCache = CacheBuilder.newBuilder()
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .build();
    
    @Override
    public void insertFill(MetaObject metaObject) {
        String cacheKey = "currentUserId";
        Long userId = (Long) valueCache.getIfPresent(cacheKey);
        if (userId == null) {
            userId = getCurrentUserId(); // 耗时操作
            valueCache.put(cacheKey, userId);
        }
        this.strictInsertFill(metaObject, "createBy", Long.class, userId);
    }
}

总结对比表

填充策略触发时机适用场景注意事项
FieldFill.INSERT仅插入操作创建时间、创建人更新操作不会触发
FieldFill.UPDATE仅更新操作更新时间、更新人插入操作不会触发
FieldFill.INSERT_UPDATE插入和更新操作最后修改时间注意业务逻辑冲突
FieldFill.DEFAULT不自动填充普通字段默认值,无需特殊处理

实战经验分享

场景一:审计字段自动填充

/**
 * 基础审计实体
 */
@Data
public class BaseAuditEntity {
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT)
    private String createUser;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateUser;
    
    @TableField(fill = FieldFill.INSERT)
    private Integer version = 0;
}

场景二:多租户数据隔离

@Component
public class TenantMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        // 自动设置租户ID
        Long tenantId = TenantContext.getCurrentTenantId();
        this.strictInsertFill(metaObject, "tenantId", Long.class, tenantId);
    }
}

MyBatis-Plus的字段自动填充功能极大地简化了公共字段的处理,通过合理的配置和使用,可以显著提高开发效率并减少代码错误。掌握 @TableField(fill = FieldFill.INSERT) 的正确用法,让你的代码更加简洁和健壮!

【免费下载链接】mybatis-plus An powerful enhanced toolkit of MyBatis for simplify development 【免费下载链接】mybatis-plus 项目地址: https://gitcode.com/gh_mirrors/my/mybatis-plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值