当前位置: 首页 > news >正文

MyBatis 类型处理器(TypeHandler)注册与映射机制:JsonListTypeHandler和JsonListTypeHandler注册时机

下面几种机制会让你的 List<String>/Map<String,?> 能正确读写成 JSON 数组/对象文本:

  1. MyBatis-Plus 自动注册
    最新版本的 MyBatis-Plus starter 会把类路径下所有带 @MappedTypes({List.class})@MappedJdbcTypes(JdbcType.VARCHAR) 这类注解的 TypeHandler 自动注册进 TypeHandlerRegistry,所以即使你不在 ConfigurationCustomizer 里再手动 registry.register(...),MyBatis-Plus 启动时也会把它们扫描进来。

  2. 字段注解强制指定
    如果你的实体里写了

    @TableField(typeHandler = JsonListTypeHandler.class) private List<String> skills;

    那 MyBatis 在构建映射的时候会直接 new 这个 JsonListTypeHandler 来处理该字段,不会再走默认的 StringTypeHandler

  3. JDBC 驱动的容错
    就算真没有任何自定义 Handler,JDBC 驱动也会把你传进去的字符串(比如你在 Mapper XML 里写 #{skills},skills.toString() 恰好是 ["a","b"])原封不动地当 VARCHAR 存到 DB,当你再查回 String 字段时,就能看到 JSON 文本。


所以你看到“即使把那两行全局注册删掉,也能正常上传/查询”,并不奇怪:

  • MyBatis-Plus Starter 在后台已经给你注册好了 List/Map 的 JSON 处理器;

  • 你要么在实体里用注解显式选了 Handler,要么 DB 和驱动自己把字符串存下来了。

如果你真想验证“到底哪条路径在生效”,可以:

  1. 断点调试 JsonListTypeHandler#setNonNullParameter(...)
    直接看看在执行插入/更新时,MyBatis 到底走的是哪个 Handler 的方法。

  2. 临时写个 ConfigurationCustomizer 打日志

    @Bean public ConfigurationCustomizer customizer() { return config -> { config.getTypeHandlerRegistry() .getTypeHandler(List.class) .ifPresent(h -> System.out.println("List 类型处理器是:" + h.getClass())); }; }

启动后控制台能看到到底用的哪个 Handler。

核心原理回顾

  1. @MappedTypes 注解
    在类上打了 @MappedTypes(List.class)@MappedTypes(Map.class),MyBatis-Plus Starter 在启动时会 扫描自动注册 这些类型处理器。

    • 如果你的版本里这个自动扫描没开,就算有 @MappedTypes,MyBatis 也不会加载它,你就必须在 ConfigurationCustomizer 里手动 registry.register(...)

  2. 字段级别指定
    如果你在实体类字段上写了

    @TableField(typeHandler = JsonListTypeHandler.class) private List<String> skills;

    那就算全局没注册,MyBatis 也会给该字段硬绑定到这个 handler 上。

  3. MyBatis-Plus 内置 JSON 处理器
    新版本里,MP 自带了基于 Jackson 的 JacksonTypeHandler,它也会给带 @MappedTypes 的类自动装上。
    所以你如果单纯删掉全局注册,MP 可能已经自己把 JsonListTypeHandlerJsonMapTypeHandler 扫进去了,看起来就“好像不用注册也行”了。


为什么你“以前删掉会报错”?

  • 旧版本 的 MP Starter 并不自动扫描你自己写的 JsonListTypeHandler,也没给 List.classMap.class 任何默认 handler。

  • 这时,插入或查询带 List<String> 字段,就会抛错:

    Type handler was null for parameter List

  • 你就逼不得已在 ConfigurationCustomizer 里手动注册,或者在字段上加 @TableField(typeHandler=…),才跑通。


现在为啥又“删了也能正常”?

  • 升级后,MP 已经把所有带 @MappedTypes 注解的 handler 自动扫描并注册 了;

  • 或者你给字段加了 @TableField(typeHandler=…),绕过了全局注册需求;

  • 又或者你的表里该列其实是 VARCHARTEXT,你直接在 service 里手动 JSON.toString(list) 存,插入成功——但是反查时就只能拿到 String。


建议

  1. 保留你的 Handler 类,毕竟它能让你自动在实体里写 List<String>Map<String,?>

  2. 如果想最简化,全局注册可以删,但要确保你的 MP Starter 版本 ≥ 3.5 且内置扫描打开:

    mybatis-plus: type-handlers-package: com.example.chat2.handler

  3. 或者在每个 JSON 字段上写:

    @TableField(typeHandler = JsonListTypeHandler.class) private List<String> skills;

    这样就算全局没扫到,也能按字段绑定。


小结

  • JsonListTypeHandler/JsonMapTypeHandler 本身是你项目必需的,只是注册方式可以有三条路:

    1. 全局 ConfigurationCustomizer 手动注册

    2. @MappedTypes + MP 自动扫描

    3. 字段上 @TableField(typeHandler=…)

  • 你“以前没写注册就报错”是因为那会儿自动扫描没开;现在自动扫描打开了,注册看似“多余”了,但在老版本或没有字段注解的情况下,它依然是必不可少的。

希望理清了为什么「看着好像多余」又「删掉又会报错」的来龙去脉!

而我是在xml写了这个

 <result property="skills" column="skills" typeHandler="com.example.chat2.handler.JsonListTypeHandler"/> 
        <result property="tools" column="tools" typeHandler="com.example.chat2.handler.JsonListTypeHandler"/>


MyBatis 会直接按照你指定的 typeHandler 来做序列化/反序列化,这种方式和在 Java 里用 @MappedTypes 或者在全局注册处理器是等价的——它们都是最终把 JSON 文本和 List<String> 对象挂钩。


是XML 显式覆盖
在 <resultMap> 或 <select> 的 <result> 里写 typeHandler="...",MyBatis 在映射这个列的时候,跳过默认的类型推断,直接 new 指定的 JsonListTypeHandler。

不再依赖自动扫描
无需再借助 @MappedTypes、type-handlers-package 或者 ConfigurationCustomizer 全局注册,都能保证该字段走你想要的 Handler。

清晰直观
只要看 XML 就知道哪些列要走 JSON 处理,不会被其他配置“隐式”影响。

何时用哪种方式?

方式优点缺点
XML 中 typeHandler最直观,按字段精确控制;不依赖额外扫描每个字段都得在 XML 定义一次,比较啰嗦
字段注解 @TableField(typeHandler=…)配置集中在实体类;配合 MP 自动生成也能生效如果你写 XML,而是用 MP 的 Wrapper/注解方式,则需要这样,XML 与注解混用时可能有重复
全局自动扫描(@MappedTypes + Starter 或者 ConfigurationCustomizer一次注册,全表所有 List/Map 列自动生效控制粒度粗,所有同类型字段都会走同一个 Handler

小贴士
如果你只在少数几个字段用 JSON,XML 显式 是最简单可靠的方式;

如果全项目大量用到,建议用 全局扫描 或者 字段注解,免得 XML 太长;

切勿同时对同一个字段在 XML、注解和全局注册里都写不同的 Handler,否则会有优先级混乱的问题。

相关文章:

  • 单例模式:全局唯一性在软件设计中的艺术实践
  • 《代码整洁之道》第6章 对象和数据结构 - 笔记
  • 04 Enhanced Telecom Operations Map (eTOM)
  • 计算机网络自顶向下思维导图
  • 《代码整洁之道》第12章 迭进 - 笔记
  • EasyRTC嵌入式音视频通信SDK助力视频客服,开启智能服务新时代
  • 嵌入式软件--stm32 DAY 4 中断系统
  • 从零开始了解数据采集(二十一)——电子制造行业趋势分析案例
  • 消防应急物资智能调用立库:豪越科技助力消防“速战速决”
  • 央视两次采访报道爱藏评级,聚焦生肖钞市场升温,评级币成交易安全“定心丸”
  • uniapp: 低功耗蓝牙(BLE)的使用
  • 《代码整洁之道》第8章 边界 - 笔记
  • JVM——垃圾收集策略
  • 【首款Armv9开源芯片“星睿“O6测评】SVE2指令集介绍与测试
  • Spring AI Alibaba - MCP连接 MySQL
  • [三分钟]web自动化测试(二):selenium自动化测试常用函数(上)
  • HarmonyOS Next~鸿蒙系统流畅性技术解析:预加载与原生架构的协同进化
  • vite.config.ts 的详细配置项说明、完整代码示例及表格总结
  • Nacos简介—4.Nacos架构和原理三
  • 英语中时间的表达
  • 李勇已任内蒙古乌兰察布市委副书记,曾在中央编办任职
  • 从 “沪惠保” 到 “沪骑保”看普惠保险的 “上海样式”
  • 银川市长信箱被指乱回复:问诗词大会、答工程欠款,官方称工作失误
  • 上海经信委:将推动整车企业转型,加强智能驾驶大模型等创新应用
  • 亚振家居控制权将变更:济南域潇集团实控人成新控股股东
  • 外交部:美国是国际军控与防扩散体系的最大破坏者