文章目录
前言
问题背景
有一些表单的字段,是根据条件类型,动态显示与填写的。这里时候可以返回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 及以上版本,并正确配置。
经验总结
问题排查流程
- 确认数据库数据 → 数据存在 ✓
- 检查实体类映射 → 字段配置正确 ✓
- 检查响应 VO → 字段存在 ✓
- 检查 Service 层转换 → 转换逻辑正确 ✓
- 检查 Mapper 查询 → 发现问题 ✗
关键知识点
-
resultTypevsresultMap:resultType:直接映射,不支持字段级类型处理器resultMap:支持复杂映射和类型处理器配置
-
autoResultMap的局限性:- 仅在使用 MyBatis Plus 内置方法时生效
- 自定义 XML 查询不会自动使用
-
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 的内部机制,希望这些经验能帮助你避免类似问题!

2875

被折叠的 条评论
为什么被折叠?



