MyBatis Plus JSON 字段无法返回问题排查与解决方案


前言

问题背景

有一些表单的字段,是根据条件类型,动态显示与填写的。这里时候可以返回extraFields 数组 [{key:‘xx’, type:‘select/text’,options:[] …按需调整}]。

在开发动态单据管理功能时,遇到了一个棘手的问题:前端传入了 extraFields 字段(JSON 类型),数据库中也正确存储了数据,但调用获取详情接口时始终无法返回该字段。

问题分析

1. 实体类配置

@TableName(value = "project_detail", autoResultMap = true)
public class ProjectDetailDO extends BaseDO {
    // ... 其他字段
    
    @TableField(typeHandler = JacksonTypeHandler.class)
    private Map<String, Object> extraFields;
    
    // ...
}

2. Mapper XML 配置

<select id="selectById" resultType="cn.iocoder.yudao.module.srm.entity.ProjectDetailDO">
    SELECT *
    FROM project_detail
    WHERE deleted = 0
      AND id = #{Id}
</select>

3. 问题根因

关键发现:虽然实体类配置了 @TableField(typeHandler = JacksonTypeHandler.class)autoResultMap = true,但当 XML 中使用 resultType 时,MyBatis 不会使用自动生成的包含类型处理器的 ResultMap。

结论resultType 直接映射属性,不会应用字段级别的类型处理器配置。

解决方案

方案一:手动定义 ResultMap(推荐)

<resultMap id="ProjectDetailMap" type="cn.iocoder.yudao.module.srm.entity.ProjectDetailDO">
    <id property="id" column="id"/>
    <!-- ... 其他字段映射 ... -->
    <result property="extraFields" column="extra_fields" 
            typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
</resultMap>

<select id="selectById" resultMap="ProjectDetailMap">
    SELECT *
    FROM project_detail
    WHERE deleted = 0
      AND id = #{Id}
</select>

方案二:使用 @Select 注解

@Mapper
public interface ProjectDetailMapper extends BaseMapperX<ProjectDetailDO> {
    
    @Select("SELECT * FROM project_detail WHERE deleted = 0 AND id = #{Id}")
    @Results(id = "ProjectDetailMap", value = {
        @Result(property = "extraFields", column = "extra_fields", 
                typeHandler = JacksonTypeHandler.class)
    })
    List<ProjectDetailDO> selectById(@Param("Id") Long Id);
}

更好的实现方案

方案三:全局配置 JSON 类型处理器

MyBatisPlusConfig 中统一配置:

@Configuration
public class MyBatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加其他插件...
        return interceptor;
    }
    
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> {
            // 为 Map 类型注册全局类型处理器
            configuration.getTypeHandlerRegistry().register(
                Map.class, 
                JacksonTypeHandler.class
            );
        };
    }
}

优点

  • 无需在每个 XML 中重复配置
  • 所有 JSON 字段自动使用 JacksonTypeHandler
  • 减少重复代码,提高可维护性

方案四:使用 MyBatis Plus 3.5+ 的新特性

MyBatis Plus 3.5.x 版本对 autoResultMap 进行了增强,确保类型处理器正确应用。

@TableName(autoResultMap = true)
public class ProjectDetailDO extends BaseDO {
    
    @TableField(typeHandler = JacksonTypeHandler.class, jdbcType = JdbcType.LONGVARCHAR)
    private Map<String, Object> extraFields;
}

注意:需要确保使用 MyBatis Plus 3.5.0 及以上版本,并正确配置。

经验总结

问题排查流程

  1. 确认数据库数据 → 数据存在 ✓
  2. 检查实体类映射 → 字段配置正确 ✓
  3. 检查响应 VO → 字段存在 ✓
  4. 检查 Service 层转换 → 转换逻辑正确 ✓
  5. 检查 Mapper 查询发现问题

关键知识点

  1. resultType vs resultMap

    • resultType:直接映射,不支持字段级类型处理器
    • resultMap:支持复杂映射和类型处理器配置
  2. autoResultMap 的局限性

    • 仅在使用 MyBatis Plus 内置方法时生效
    • 自定义 XML 查询不会自动使用
  3. JSON 字段处理最佳实践

    • 推荐使用全局类型处理器配置
    • 或在每个自定义查询中显式指定类型处理器

代码优化建议

// 推荐:使用泛型定义,提高类型安全性
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> extraFields;

// 更佳:使用具体的 DTO 类型
@TableField(typeHandler = JacksonTypeHandler.class)
private ProjectExtraFieldsDTO extraFields;

结语

MyBatis Plus 的 JSON 字段处理需要特别注意类型处理器的配置方式。在遇到类似问题时,应优先检查查询语句的返回配置,确保类型处理器正确应用。全局配置是更优雅的解决方案,值得在项目中推广使用。


参考资料
https://mybatis.org/mybatis-3/

通过这次问题排查,深入理解了 MyBatis Plus 的内部机制,希望这些经验能帮助你避免类似问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TE-茶叶蛋

踩坑不易,您的打赏,感谢万分

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

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

抵扣说明:

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

余额充值