news 2026/5/6 8:34:07

MyBatis 扩展BaseTypeHandler 转换泛型 JSON 列表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 11:42:45

WPF 轻量级工业边缘网关:支持 PLC 数据采集、条件触发与 HTTP 上报

前言工业自动化领域&#xff0c;设备数据的采集、处理与上传是开发智能监控系统的基础。面对不同品牌 PLC、协议不统一、开发门槛高、部署复杂等问题&#xff0c;常常让大家不知所措。有没有一种工具&#xff0c;既能图形化配置设备连接&#xff0c;又能灵活定义数据处理逻辑&a…

作者头像 李华
网站建设 2026/5/3 7:00:38

OpenCVSharp:学习最佳匹配矩形检测

前言今天来学习一下OpenCVSharp中最佳匹配矩形检测的例子。其过程可以分为ORB特征检测、特征匹配、最佳匹配筛选、单应性计算与矩形绘制。效果&#xff1a;实践ORB特征检测进行ORB特征检测&#xff1a;using var img1 new Mat(FirstImagePath, ImreadModes.Color); usingvar i…

作者头像 李华
网站建设 2026/5/1 2:57:21

15、数据类型转换函数与元素提取详解

数据类型转换函数与元素提取详解 1. 输入值与格式规范中的分隔符 虽然建议输入值和格式规范使用相同的分隔符(这样更清晰),但 Oracle 对此要求并不严格,分隔符甚至在间距方面都可以不同。以下两个示例均能正常工作: select TO_DATE(15.10.2020, DD/MM/YYYY) from dual…

作者头像 李华
网站建设 2026/5/6 7:10:14

LLama-Factory集成HuggingFace镜像,加速模型下载提升训练效率

LLama-Factory集成HuggingFace镜像&#xff0c;加速模型下载提升训练效率 在大语言模型&#xff08;LLM&#xff09;快速发展的今天&#xff0c;微调已成为将通用预训练模型转化为行业专用智能体的核心手段。然而&#xff0c;现实中的开发者常常面临两个“拦路虎”&#xff1a;…

作者头像 李华
网站建设 2026/5/4 19:26:37

AutoGPT与Zapier集成可能性分析:连接上千种SaaS服务的桥梁

AutoGPT与Zapier集成&#xff1a;构建智能自动化代理的实践路径 在企业系统日益碎片化的今天&#xff0c;一个常见的困境是&#xff1a;我们拥有强大的AI语言模型&#xff0c;能写出流畅报告、设计学习计划&#xff0c;甚至模拟决策逻辑&#xff0c;却难以让它真正“动手”——…

作者头像 李华
网站建设 2026/4/30 18:11:25

中国科学技术大学论文模板参考文献格式调整深度解析与实用指南

中国科学技术大学论文模板参考文献格式调整深度解析与实用指南 【免费下载链接】ustcthesis LaTeX template for USTC thesis 项目地址: https://gitcode.com/gh_mirrors/us/ustcthesis 中国科学技术大学论文模板&#xff08;ustcthesis&#xff09;近期针对参考文献格式…

作者头像 李华