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

MyBatis 扩展BaseTypeHandler 转换泛型 JSON 列表

最近发现一个mybatis里面json转换的bug, 写了这么多年Java这方面还是没有理清楚, 把正确的处理方法记录一下.

一. 对象JSON转换

这个是比较简单的情况, 有通用的处理方法, 例如

用Jackson实现一个通用的 TypeHandler

@Slf4j public class JacksonTypeHandler<T> extends BaseTypeHandler<T> { private static ObjectMapper OBJECT_MAPPER; private final Class<T> clazz; public JacksonTypeHandler(Class<T> clazz) { if (log.isTraceEnabled()) { log.trace("JacksonTypeHandler[{}]", clazz); } this.clazz = clazz; } @Override public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, toJson(parameter)); } @Override public T getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } @Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } @Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } protected T parse(String json) { if (json == null || json.isEmpty()) return null; try { return getObjectMapper().readValue(json, clazz); } catch (IOException e) { throw new RuntimeException(e); } } protected String toJson(T obj) { try { return getObjectMapper().writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } private static ObjectMapper getObjectMapper() { if (null == OBJECT_MAPPER) { OBJECT_MAPPER = new ObjectMapper(); OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); } return OBJECT_MAPPER; } }

使用时直接指定 typeHandler 就行

<result property="groupFilter" column="group_filter" typeHandler="com.somewhere.mybatis.JacksonTypeHandler"/>

以及

#{groupFilter, typeHandler=com.somewhere.mybatis.JacksonTypeHandler},

二. 列表JSON Array转换

字段中更常见的是 List 类型的JSON Array结构, 这种情况通过扩展 BaseTypeHandler 没有通用的处理方法, 有两种实现途径

通用的TypeHandler, 需要指定javaType

用 Jackson 实现一个通用的 TypeHandler, 注意构造函数中的 clazz, 如果不指定, 在默认情况下 mybatis 传进来的是一个不带泛型参数的 List

@Slf4j public class JacksonListTypeHandler<T> extends BaseTypeHandler<List<T>> { private static ObjectMapper OBJECT_MAPPER; private final TypeReference<List<T>> type; public JacksonListTypeHandler(Class<T> clazz) { log.info("JacksonListTypeHandler[{}]", clazz); this.type = new TypeReference<>() { @Override public Type getType() { // 返回参数化类型 List<T> return new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[]{clazz}; } @Override public Type getRawType() { return List.class; } @Override public Type getOwnerType() { return null; } }; } }; } @Override public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, toJson(parameter)); } @Override public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } @Override public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } @Override public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } private List<T> parse(String json) { if (json == null || json.isEmpty()) return null; try { return getObjectMapper().readValue(json, type); } catch (IOException e) { throw new RuntimeException(e); } } protected String toJson(List<T> obj) { try { return getObjectMapper().writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } public static ObjectMapper getObjectMapper() { if (null == OBJECT_MAPPER) { OBJECT_MAPPER = new ObjectMapper(); OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); } return OBJECT_MAPPER; } }

除了用 TypeReference, 还可以用 JavaType

@Slf4j public class ListJacksonTypeHandler<T> extends BaseTypeHandler<List<T>> { private final JavaType type; public ListJacksonTypeHandler(Class<T> clazz) { log.info("ListJacksonTypeHandler[{}]", clazz); // 创建 List<T> 类型的 JavaType this.type = JacksonUtil.getObjectMapper() .getTypeFactory() .constructCollectionType(List.class, clazz); } // 其它一样 }

需要在 mapper 中强制指定javaType, 如果是MyBatis自带的简单类型, 可以直接用 alias(见下面的表格), 如果是自定义的对象, 则需要用对象类的完整包路径

<result property="ips" column="ips" javaType="string" typeHandler="com.somewhere.mybatis.JacksonListTypeHandler"/>

以及

#{users,javaType=string,typeHandler=com.somewhere.mybatis.JacksonListTypeHandler},

这样启动后, 如果初始化的type是正确的string 才能正确解析

2025-12-15T10:33:42.896+08:00 INFO 1 --- [some-service] [main] c.somewhere.mybatis.JacksonListTypeHandler: JacksonListTypeHandler[class java.lang.String]

抽象类, 根据对象类型实现具体的 TypeHandler

如果不在 mapper 中指定类型, 就需要在 TypeHandler 中指定, 这样就不是通用的了

写一个基类 AbstractListTypeHandler

@Slf4j public abstract class AbstractListTypeHandler<T> extends BaseTypeHandler<List<T>> { private static ObjectMapper OBJECT_MAPPER; @Override public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, toJson(parameter)); } @Override public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } @Override public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } @Override public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } protected List<T> parse(String json) { if (json == null || json.isEmpty()) return null; try { return getObjectMapper().readValue(json, specificType()); } catch (IOException e) { throw new RuntimeException(e); } } protected String toJson(List<T> obj) { try { return getObjectMapper().writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } private static ObjectMapper getObjectMapper() { if (null == OBJECT_MAPPER) { OBJECT_MAPPER = new ObjectMapper(); OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); } return OBJECT_MAPPER; } protected abstract TypeReference<List<T>> specificType(); }

根据具体的使用场景, 创建对应的实现类

public class ListLongTypeHandler extends AbstractListTypeHandler<Long> { @Override protected TypeReference<List<Long>> specificType() { return new TypeReference<>() {}; } }

使用时, 直接用 typeHandler 指定, 不需要指定类型

#{setIds, typeHandler=com.somewhere.mybatis.ListLongTypeHandler}, ... @Result(column="set_ids", property="setIds", typeHandler= ListLongTypeHandler.class),

附: MyBatis 内建的 javaType Alias

链接: https://mybatis.org/mybatis-3/configuration.html#typeAliases

aliasjavaType
_bytebyte
_char (since 3.5.10)char
_character (since 3.5.10)char
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
char (since 3.5.10)Character
character (since 3.5.10)Character
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
bigintegerBigInteger
objectObject
date[]Date[]
decimal[]BigDecimal[]
bigdecimal[]BigDecimal[]
biginteger[]BigInteger[]
object[]Object[]
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator
http://www.cnnetsun.cn/news/75088.html

相关文章:

  • PKHeX.Mobile终极指南:移动端宝可梦存档编辑完全教程
  • 21、时间点与持续时间建模:原理、应用与挑战
  • 如何快速掌握网站下载:WebSite-Downloader 完整使用指南
  • 27、时态数据库概念解析
  • 30、数据库镜像重建中的闪回管理与SQL注入防范
  • Wan2.2-T2V-5B模型API封装实践:集成到Web应用的步骤详解
  • Typora支持HTML5音频播放:完美呈现ACE-Step输出效果
  • 39、使用MRTG进行网络监控的全面指南
  • AI+教育的未来发展趋势:人机环境系统智能
  • 8个降AI率工具推荐,本科生课堂汇报必备
  • 1、雾无线接入网络:5G 及 6G 通信的新范式
  • 卫星轨道预测终极指南:5步掌握SGP4模型核心应用
  • 中国科学技术大学学位论文LaTeX模板完整指南
  • 16、雾无线接入网络中的计算卸载与原型设计
  • 18、雾无线接入网络(F-RAN):原型、性能评估与未来挑战
  • Snakemake中的样本配对与文件处理
  • 工业防水平板电脑WPPC-H1520T(P)在食品加工湿环境中的部署与应用开发实践
  • 探索数字组合的艺术
  • Shiny模块化开发:解决Tab选中问题
  • AutoGPT与SQLite轻量数据库集成:适用于小型项目的本地存储方案
  • Jenkins Pipeline调用LLama-Factory训练任务,实现无人值守AI训练
  • 中小学教育AI工具开发:架构师的数据主权方案
  • 如何快速上手 Harepacker-resurrected:从入门到精通的完整指南
  • 百万 Token 也能无损压缩?C3 模型用“级联压缩”重新定义长上下文挑战
  • GOBI 2025 全球开源商业创新大会顶级嘉宾阵容公开!4 大 Panel 火力全开
  • 安卓私密文件同步终极方案:Syncthing-Android完全指南
  • AutoClicker鼠标自动化工具:新手完全指南与实战技巧
  • 鸿蒙原子化服务新玩法:Flutter也能开发高性能Service卡片
  • 8 个 MBA 毕业答辩 PPT 工具,AI 格式优化推荐
  • 如何快速实现STL转STEP:面向3D设计新手的完整指南