首頁 > 軟體

MySQL讀取JSON轉換的方式

2022-03-17 19:02:27

儲存

mysql5.7+開始支援儲存JSON,後續不斷優化,應用也越來越廣泛
你可以自己將資料轉換成Json String後插入,也可以選擇使用工具,
而mybatis-plus就為此提供了非常簡便的方式,
只需要在欄位上加上 @TableField(typeHandler = XxxTypeHandler.class),
mybatis-plus就會自動幫你做轉換,通用一般就兩個:
   - com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler
   - com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler

例如

@Data
@TableName(autoResultMap = true)
public class Department implements Serializable {
    private static final long serialVersionUID = 203788572415896870L;
    @TableField(typeHandler = FastjsonTypeHandler.class)
    private List<Integer> userIds;
}

存在什麼問題?

如果使用通用處理器,那對於基礎型別以及物件來說沒有什麼問題。
但如果儲存的欄位型別是物件集合,那麼當你取出來時,會發現集合中的物件都是JSONObject型別。
最常見的情況就拿出來進行遍歷操作時,會丟擲強轉異常:
    java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to ...
因為處理器幫你轉換時,並不會儲存你集合的泛型,所以統統都按照Object型別來轉換了:
    @Override
    protected Object parse(String json) {
        return JSON.parseObject(json, type);
    }

例如下面這種形式的類:

@Data
@TableName(autoResultMap = true)
public class Department implements Serializable {

    private static final long serialVersionUID = 203788572415896870L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @TableField(typeHandler = FastjsonTypeHandler.class)
    private List<User> users;
    @Data
    public static class USer implements Serializable {
        // ...
    }
}

如何處理

方式一:自定義處理器,自己做型別轉換,這也是當前最普遍的方式,但是對於存在List欄位的物件,還需要在XxxMapper.xml中進行resultMap設定

@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ListFastJsonTypeHandler extends FastjsonTypeHandler {

    private final Class<? extends Object> type;
    public ListFastJsonTypeHandler(Class<?> type) {
        super(type);
        this.type = type;
    }
    /**
     * 自己將json轉換成list
     * @param json
     * @return
     */
    @Override
    protected Object parse(String json) {
        return JSON.parseArray(json, this.type);
}
<mapper namespace="com.xxx.cn.mapper.DepartmentMapper">
    <resultMap id="BaseResultMap" type="com.xxx.cn.domain.Department">
        <id property="id" column="id"/>
        <result property="users" column="users" jdbcType="VARCHAR"
                javaType="com.xxx.cn.domain.Department.User"
                typeHandler="com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler"/>
    </resultMap>
</mapper>

設定完成後,ListFastJsonTypeHandler就會將json轉換成javaType對應的物件集合了

方式二:設定一個Mybatis外掛,攔截ResultSetHandler,將返回結果進行處理。 這樣的好處就是不用寫自定義的處理器和在XxxMapper.xml中做設定,減少了工作

@Component
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultSetInterceptor implements Interceptor {

    /**
     * json序列化規則
     */
    private final SerializerFeature[] serializerFeatures = {
            SerializerFeature.WriteMapNullValue,
            SerializerFeature.WriteNullListAsEmpty,
            SerializerFeature.WriteNullStringAsEmpty
    };
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object proceed = invocation.proceed();
        if (containJSONObject(proceed)) {
            if (proceed instanceof Collection) {
                return JSON.parseArray(JSON.toJSONString(proceed, serializerFeatures), ((Collection<?>) proceed).toArray()[0].getClass());
            }
            return JSON.parseObject(JSON.toJSONString(proceed, serializerFeatures), proceed.getClass());
        }
//        if (proceed instanceof Collection) {
//            for (Object obj : ((Collection<?>) proceed)) {
//                parseJSON2Object(obj, obj.getClass());
//            }
//        } else {
//            parseJSON2Object(proceed, proceed.getClass());
//        }
        return proceed;
    }
     * 將返回資料中心的JSONObject物件轉換成正常的物件
     *
     * @param obj
     * @param typeClass
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
    private void parseJSON2Object(Object obj, Class<?> typeClass) throws IllegalAccessException, ClassNotFoundException {
        for (Field declaredField : typeClass.getDeclaredFields()) {
            declaredField.setAccessible(true);
            Object value = declaredField.get(obj);
            if (isNullValueField(value)) {
                continue;
            Type genericType = declaredField.getGenericType();
            String fieldClassName = genericType.getTypeName();
            if (genericType instanceof ParameterizedType) {
                fieldClassName = ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName();
            if (containJSONObject(value)) {
                if (value instanceof Collection) {
                    declaredField.set(obj, JSON.parseArray(JSON.toJSONString(value, serializerFeatures), Class.forName(fieldClassName)));
                } else {
                    declaredField.set(obj, JSON.parseObject(JSON.toJSONString(value, serializerFeatures), Class.forName(fieldClassName)));
                }
     * 判斷是否跳過欄位
     * @param value
     * @return
    private Boolean isNullValueField(Object value) {
        return null == value || "".equals(String.valueOf(value).trim())
                || (value instanceof Collection && ((Collection<?>) value).size() == 0);
     * 判斷值是否包含JSONObject物件
    private boolean containJSONObject(Object value) throws IllegalAccessException {
        if (isNullValueField(value)) {
            return false;
        if (value instanceof Collection) {
            for (Object obj : (Collection<?>) value) {
                if (obj instanceof JSONObject) {
                    return true;
                if (obj instanceof Collection && containJSONObject(obj)) {
                    for (Field declaredField : obj.getClass().getDeclaredFields()) {
                        declaredField.setAccessible(true);
                        Object fieldValue = declaredField.get(obj);
                        if (isNullValueField(fieldValue)) {
                            continue;
                        }
                        if (fieldValue instanceof JSONObject) {
                            return true;
                        if (fieldValue instanceof Collection && containJSONObject(fieldValue)) {
                    }
        return value instanceof JSONObject;
}

到此這篇關於MySQL讀取JSON轉換的文章就介紹到這了,更多相關MySQL讀取JSON轉換內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com