网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 后端开发 > Java >

MyBatis 扩展BaseTypeHandler 转换泛型 JSON 列表

时间:2025-12-15 23:55

人气:

作者:admin

标签:

导读:最近发现一个mybatis里面json转换的bug, 写了这么多年Java这方面还是没有理清楚, 把正确的处理方法记录一下. 一. 对象JSON转换 这个是比较简单的情况, 有通用的处理方法, 例如 用Jackson实现...

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

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

用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},

字段中更常见的是 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),

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

alias javaType
_byte byte
_char (since 3.5.10) char
_character (since 3.5.10) char
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
char (since 3.5.10) Character
character (since 3.5.10) Character
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
biginteger BigInteger
object Object
date[] Date[]
decimal[] BigDecimal[]
bigdecimal[] BigDecimal[]
biginteger[] BigInteger[]
object[] Object[]
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator
温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信