Mybatis-Plus MetaObjectHandler基操及源码解析


参考【 https://baomidou.com/guides/auto-fill-field/

在 MyBatis-Plus 中提供了一个便捷的自动填充功能。MetaObjectHandler 是一个用于处理实体对象的元对象的接口。它允许我们在插入、更新、删除等操作之前或之后,对实体对象进行自定义处理。通过 MetaObjectHandler,我们可以实现如自动填充创建时间、更新时间、删除标记等功能。

一、基操

1.1 实现 MetaObjectHandler接口

在上面的示例中,我们使用了 @Component 注解,确保MyMetaObjectHandler类被 Spring 管理。我们创建了一个名为MyMetaObjectHandler 的类,并实现了 MetaObjectHandler 接口。在insertFill方法中,我们自动填充了创建人和创建时间。在 updateFill方法中,我们自动更新数据库表中更新时间与更新人。

/**
 * 通用字段处理器
 */
@Component
@Slf4j
public class CommonFieldHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        UserBean userBean = LoginUtil.get();
        if (userBean != null) {
            // 创建人ID
            setFieldValByName("createUserId", LoginUtil.get().getId(), metaObject);
            // 创建人名称
            setFieldValByName("createUserName", LoginUtil.get().getName(), metaObject);
            // 创建时间
            setFieldValByName("createTime", new Date(), metaObject);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        UserBean userBean = LoginUtil.get();
        if (userBean != null) {
            // 更新人ID
            setFieldValByName("updateUserId", LoginUtil.get().getId(), metaObject);
            // 更新人名称
            setFieldValByName("updateUserName", LoginUtil.get().getName(), metaObject);
            // 更新时间
            setFieldValByName("updateTime", new Date(), metaObject);
        }
    }
}

在上面的示例中,将 CommonFieldHandler 类交由 Spring IoC容器进行管理。在insertFill方法中,自动填充了创建人和创建时间。在 updateFill方法中,自动填充更新人与更新时间。

1.2 使用 MetaObjectHandler

在实体类中,通过 @TableFile(fill = FieldFill.INSERT) 添加创建人、创建时间,通过 @TableField(fill = FieldFill.UPDATE) 添加更新人、更新时间字段,如下:

@Data
public class BaseBean implements Serializable {
    private static final long serialVersionUID = -63039799721058887L;
    @ApiModelProperty(value = "id")
    @TableId(type = IdType.AUTO)
    private Long id;

    @ApiModelProperty(value = "创建人ID")
    @TableField(fill = FieldFill.INSERT)
    private Long createUserId;

    @ApiModelProperty(value = "创建人名称")
    @TableField(fill = FieldFill.INSERT)
    private String createUserName;

    @ApiModelProperty(value = "创建时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @ApiModelProperty(value = "更新人ID")
    @TableField(fill = FieldFill.UPDATE)
    private Long updateUserId;

    @ApiModelProperty(value = "更新人名称")
    @TableField(fill = FieldFill.UPDATE)
    private String updateUserName;

    @ApiModelProperty(value = "更新时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @TableField(fill = FieldFill.UPDATE)
    private Date updateTime;
}

1.3 配置自动填充处理器

确保你的 MyMetaObjectHandler 类被 Spring 管理,可以通过 @Component@Bean 注解等来实现。

1.4 使用事项

  • 自动填充是直接给实体类的属性设置值。
  • 如果属性没有值,入库时会是 null。
  • MetaObjectHandler 提供的默认方法策略是:如果属性有值则不覆盖,如果填充值为 null 则不填充。
  • 字段必须声明 @TableField 注解,并设置 fill 属性来选择填充策略。
  • 使用 strictInsertFillstrictUpdateFill 方法可以根据注解 FieldFill.xxxx、字段名和字段类型来区分填充逻辑。
  • 如果不需区分,可以使用 fillStrategy 方法。
  • update(T entity, Wrapper<T> updateWrapper) 时,entity 不能为空,否则自动填充失效。
  • update(Wrapper<T> updateWrapper) 时不会自动填充,需要手动赋值字段条件。

1.5 FieldFill

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

二、源码解析

2.1 MetaObjectHandler源码

public interface MetaObjectHandler {

    // 是否开启了插入填充
    default boolean openInsertFill() {
        return true;
    }

    // 是否开启了更新填充
    default boolean openUpdateFill() {
        return true;
    }

    // 插入元对象字段填充(用于插入时对公共字段的填充)
    void insertFill(MetaObject metaObject);

    // 更新元对象字段填充(用于更新时对公共字段的填充)
    void updateFill(MetaObject metaObject);

    /**
     * 通用填充
     */
    default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
        if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
        return this;
    }

    // 获取当前字段value
    default Object getFieldValByName(String fieldName, MetaObject metaObject) {
        return metaObject.hasGetter(fieldName) ? metaObject.getValue(fieldName) : null;
    }

    /**
     * find the tableInfo cache by metaObject
     */
    default TableInfo findTableInfo(MetaObject metaObject) {
        return TableInfoHelper.getTableInfo(metaObject.getOriginalObject().getClass());
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default <T, E extends T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {
        return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default <T, E extends T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {
        return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default MetaObjectHandler strictInsertFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        return strictFill(true, tableInfo, metaObject, strictFills);
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default <T, E extends T> MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {
        return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default <T, E extends T> MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {
        return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));
    }

    /**
     * @param metaObject metaObject meta object parameter
     */
    default MetaObjectHandler strictUpdateFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        return strictFill(false, tableInfo, metaObject, strictFills);
    }

    /**
     * 严格填充,只针对非主键的字段,只有该表注解了fill 并且 字段名和字段属性 能匹配到才会进行填充(null 值不填充)
     *
     * @param insertFill  是否验证在 insert 时填充
     * @param tableInfo   cache 缓存
     * @param metaObject  metaObject meta object parameter
     * @param strictFills 填充信息
     * @return this
     * @since 3.3.0
     */
    default MetaObjectHandler strictFill(boolean insertFill, TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {
        if ((insertFill && tableInfo.isWithInsertFill()) || (!insertFill && tableInfo.isWithUpdateFill())) {
            strictFills.forEach(i -> {
                final String fieldName = i.getFieldName();
                final Class<?> fieldType = i.getFieldType();
                tableInfo.getFieldList().stream()
                    .filter(j -> j.getProperty().equals(fieldName) && fieldType.equals(j.getPropertyType()) &&
                        ((insertFill && j.isWithInsertFill()) || (!insertFill && j.isWithUpdateFill()))).findFirst()
                    .ifPresent(j -> strictFillStrategy(metaObject, fieldName, i.getFieldVal()));
            });
        }
        return this;
    }

    /**
     * 填充策略,默认有值不覆盖,如果提供的值为null也不填充
     */
    default MetaObjectHandler fillStrategy(MetaObject metaObject, String fieldName, Object fieldVal) {
        if (getFieldValByName(fieldName, metaObject) == null) {
            setFieldValByName(fieldName, fieldVal, metaObject);
        }
        return this;
    }

    /**
     * 严格模式填充策略,默认有值不覆盖,如果提供的值为null也不填充
     */
    default MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
        if (metaObject.getValue(fieldName) == null) {
            Object obj = fieldVal.get();
            if (Objects.nonNull(obj)) {
                metaObject.setValue(fieldName, obj);
            }
        }
        return this;
    }
}

2.2 扫描/获取所有MetaObjectHandler对象

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
	
	/**
	 * 注入一个SqlSessionFactory Bean
	 */
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    	// 省略其他代码...
		
        // TODO 注入填充器
        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
        
    	// 省略其他代码...
	}
	
	/**
     * 检查spring容器里是否有对应的bean,有则进行消费
     *
     * @param clazz    class
     * @param consumer 消费
     * @param <T>      泛型
     */
    private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {
        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {
            consumer.accept(this.applicationContext.getBean(clazz));
        }
    }
}

2.3 MybatisParameterHandler

通过 Mybatis 提供的参数处理器 ParameterHandler 进行赋值,ParameterHandler 主要是解析 Sql 参数和用户传入参数,然后两者进行关联并设置到 PreparedStatement 中去。

MybatisParameterHandler 源码如下:

public class MybatisParameterHandler implements ParameterHandler {

public MybatisParameterHandler(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) {
		// 省略其他代码...
	
		// 查询的参数信息
        this.parameterObject = processParameter(parameter);
    }
	
    public Object processParameter(Object parameter) {
        /* 只处理插入或更新操作 */
        if (parameter != null
            && (SqlCommandType.INSERT == this.sqlCommandType || SqlCommandType.UPDATE == this.sqlCommandType)) {
            //检查 parameterObject
            if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
                return parameter;
            }
            Collection<Object> parameters = getParameters(parameter);
            if (null != parameters) {
                parameters.forEach(this::process);
            } else {
                process(parameter);
            }
        }
        return parameter;
    }

	private void process(Object parameter) {
        if (parameter != null) {
            TableInfo tableInfo = null;
            Object entity = parameter;
            if (parameter instanceof Map) {
                // 处理单参数使用注解标记的时候,尝试提取et来获取实体参数
                Map<?, ?> map = (Map<?, ?>) parameter;
                if (map.containsKey(Constants.ENTITY)) {
                    Object et = map.get(Constants.ENTITY);
                    if (et != null) {
                        entity = et;
                        tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
                    }
                }
            } else {
                tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());
            }
            if (tableInfo != null) {
                //到这里就应该转换到实体参数对象了,因为填充和ID处理都是针对实体对象处理的,不用传递原参数对象下去.
                MetaObject metaObject = this.configuration.newMetaObject(entity);
                if (SqlCommandType.INSERT == this.sqlCommandType) {
                    populateKeys(tableInfo, metaObject, entity);
                    insertFill(metaObject, tableInfo);
                } else {
                    updateFill(metaObject, tableInfo);
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值