2026/1/11 10:39:43
网站建设
项目流程
小网站怎么建设,导购网站 模板,深圳东门买衣服攻略,网站流量怎么做乡1万最近发现一个mybatis里面json转换的bug, 写了这么多年Java这方面还是没有理清楚, 把正确的处理方法记录一下. 一. 对象JSON转换 这个是比较简单的情况, 有通用的处理方法, 例如 用Jackson实现一个通用的 TypeHandler Slf4j
public class JacksonTypeHandlerT extends B…最近发现一个mybatis里面json转换的bug, 写了这么多年Java这方面还是没有理清楚, 把正确的处理方法记录一下.一. 对象JSON转换这个是比较简单的情况, 有通用的处理方法, 例如用Jackson实现一个通用的 TypeHandlerSlf4j public class JacksonTypeHandlerT extends BaseTypeHandlerT { private static ObjectMapper OBJECT_MAPPER; private final ClassT clazz; public JacksonTypeHandler(ClassT 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 propertygroupFilter columngroup_filter typeHandlercom.somewhere.mybatis.JacksonTypeHandler/以及#{groupFilter, typeHandlercom.somewhere.mybatis.JacksonTypeHandler},二. 列表JSON Array转换字段中更常见的是 List 类型的JSON Array结构, 这种情况通过扩展 BaseTypeHandler 没有通用的处理方法, 有两种实现途径通用的TypeHandler, 需要指定javaType用 Jackson 实现一个通用的 TypeHandler, 注意构造函数中的 clazz, 如果不指定, 在默认情况下 mybatis 传进来的是一个不带泛型参数的 ListSlf4j public class JacksonListTypeHandlerT extends BaseTypeHandlerListT { private static ObjectMapper OBJECT_MAPPER; private final TypeReferenceListT type; public JacksonListTypeHandler(ClassT clazz) { log.info(JacksonListTypeHandler[{}], clazz); this.type new TypeReference() { Override public Type getType() { // 返回参数化类型 ListT 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, ListT parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, toJson(parameter)); } Override public ListT getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } Override public ListT getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } Override public ListT getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } private ListT 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(ListT 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, 还可以用 JavaTypeSlf4j public class ListJacksonTypeHandlerT extends BaseTypeHandlerListT { private final JavaType type; public ListJacksonTypeHandler(ClassT clazz) { log.info(ListJacksonTypeHandler[{}], clazz); // 创建 ListT 类型的 JavaType this.type JacksonUtil.getObjectMapper() .getTypeFactory() .constructCollectionType(List.class, clazz); } // 其它一样 }需要在 mapper 中强制指定javaType, 如果是MyBatis自带的简单类型, 可以直接用 alias(见下面的表格), 如果是自定义的对象, 则需要用对象类的完整包路径result propertyips columnips javaTypestring typeHandlercom.somewhere.mybatis.JacksonListTypeHandler/以及#{users,javaTypestring,typeHandlercom.somewhere.mybatis.JacksonListTypeHandler},这样启动后, 如果初始化的type是正确的string 才能正确解析2025-12-15T10:33:42.89608:00 INFO 1 --- [some-service] [main] c.somewhere.mybatis.JacksonListTypeHandler: JacksonListTypeHandler[class java.lang.String]抽象类, 根据对象类型实现具体的 TypeHandler如果不在 mapper 中指定类型, 就需要在 TypeHandler 中指定, 这样就不是通用的了写一个基类 AbstractListTypeHandlerSlf4j public abstract class AbstractListTypeHandlerT extends BaseTypeHandlerListT { private static ObjectMapper OBJECT_MAPPER; Override public void setNonNullParameter(PreparedStatement ps, int i, ListT parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, toJson(parameter)); } Override public ListT getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } Override public ListT getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } Override public ListT getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } protected ListT 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(ListT 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 TypeReferenceListT specificType(); }根据具体的使用场景, 创建对应的实现类public class ListLongTypeHandler extends AbstractListTypeHandlerLong { Override protected TypeReferenceListLong specificType() { return new TypeReference() {}; } }使用时, 直接用 typeHandler 指定, 不需要指定类型#{setIds, typeHandlercom.somewhere.mybatis.ListLongTypeHandler}, ... Result(columnset_ids, propertysetIds, typeHandler ListLongTypeHandler.class),附: MyBatis 内建的 javaType Alias链接: https://mybatis.org/mybatis-3/configuration.html#typeAliasesaliasjavaType_bytebyte_char (since 3.5.10)char_character (since 3.5.10)char_longlong_shortshort_intint_integerint_doubledouble_floatfloat_booleanbooleanstringStringbyteBytechar (since 3.5.10)Charactercharacter (since 3.5.10)CharacterlongLongshortShortintIntegerintegerIntegerdoubleDoublefloatFloatbooleanBooleandateDatedecimalBigDecimalbigdecimalBigDecimalbigintegerBigIntegerobjectObjectdate[]Date[]decimal[]BigDecimal[]bigdecimal[]BigDecimal[]biginteger[]BigInteger[]object[]Object[]mapMaphashmapHashMaplistListarraylistArrayListcollectionCollectioniteratorIterator