提供一種在 xml 中指定使用 XxxTypeHandler 映射 Java 對象(含泛型)的方式

背景

Java 項目柬批,使用 Mybatis Plus荆隘,在 xml 中使用了 JSON_ARRAY/JSON_ARRAYAGG 函數(shù)精偿,想直接映射成 Java 實體蜒谤,勢必想到要用 JacksonTypeHandler棘劣,但是有一個問題俏让,就是對于復雜類型(含泛型),就沒法在 xml 中通過 javaType 的方式告知 JacksonTypeHandler 了茬暇,遂導致解析失敗首昔。

解決

MyJacksonTypeHandler.java

public class MyJacksonTypeHandler extends JacksonTypeHandler {

    private final Class<?> type;

    public MyJacksonTypeHandler(Class<?> type) {
        super(type);
        this.type = type;
    }

    protected Object parse(String json) {
        try {
            if (MyTypeReference.class.isAssignableFrom(type)) {
                MyTypeReference typeReference = (MyTypeReference) type.getConstructor().newInstance();
                return getObjectMapper().readValue(json, typeReference.getType());
            }

            return getObjectMapper().readValue(json, this.type);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

MyTypeReference.java

public interface MyTypeReference {

    TypeReference<?> getType();

}

TestJsonList.java

@Data
public class TestJsonList {

    private Integer id;

    private List<Dog> jsonInfo;

    @Data
    public static class Dog {
        private String name;
    }

    public static class DogListTypeReference implements MyTypeReference {
        @Override
        public TypeReference<?> getType() {
            return new TypeReference<List<Dog>>(){};
        }
    }

}

TestJsonMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xxx.mapper.TestJsonMapper">
    <resultMap id="testJsonResultMap" type="xxx.model.TestJson">
        <result property="jsonInfo" column="json_info" jdbcType="LONGVARCHAR"
                javaType="xxx.model.TestJson$Dog"
                typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
    </resultMap>

    <resultMap id="testJsonListResultMap" type="xxx.model.TestJsonList">
        <result property="jsonInfo" column="json_info" jdbcType="LONGVARCHAR"
                javaType="xxx.model.TestJsonList$DogListTypeReference"
                typeHandler="xxx.handler.MyJacksonTypeHandler"/>
    </resultMap>

    <select id="testJson" resultMap="testJsonResultMap">
        select 1 as id, '{"name": "dahuang"}' as json_info
    </select>

    <select id="testJsonList" resultMap="testJsonListResultMap">
        select 1 as id, '[{"name": "dahuang"}]' as json_info
    </select>
</mapper>

后續(xù)

實際測試過程中,發(fā)現(xiàn)使用了 LEFT JOIN 后糙俗,得到的 json 的所有 value 都會 null勒奇,解析后就得到一個所有成員都為 null 的對象,需要過濾下:

MyJacksonTypeHandler.java

public class MyJacksonTypeHandler extends JacksonTypeHandler {

    private final Class<?> type;

    public MyJacksonTypeHandler(Class<?> type) {
        super(type);
        this.type = type;
    }

    @Override
    protected Object parse(String json) {
        try {
            // 如果指定了 MyTypeReference 類型巧骚,需要特殊處理
            if (MyTypeReference.class.isAssignableFrom(type)) {
                MyTypeReference typeReference = (MyTypeReference) type.getConstructor().newInstance();
                Object result = getObjectMapper().readValue(json, typeReference.getType());
                // 由于使用了 LEFT JOIN赊颠,右表可能為空,導致這里的 json 所有 value 都為 null劈彪,單獨處理下
                if (isAllFieldNull(result)) {
                    return null;
                } else {
                    removeNullItem(result);
                    return result;
                }
            }

            // 否則竣蹦,使用默認的處理方式就行
            return getObjectMapper().readValue(json, type);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 去除 List 中對象成員均為 null 的項
     */
    private void removeNullItem(Object object) {
        Class<?> clazz = object.getClass();
        if (List.class.isAssignableFrom(clazz)) {
            ((List<?>) object).removeIf(this::isAllFieldNull);
        }
    }

    /**
     * 判斷對象是否所有成員都為 null
     */
    private boolean isAllFieldNull(Object object) {
        Class<?> clazz = object.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            try {
                if (field.get(object) != null) {
                    return false;
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        return true;
    }

}

補充

如果不在 XxxMapper.xml 中書寫 sql,可以直接在 Java 類上指定 JacksonTypeHandler 即可沧奴,也能處理復雜類型(含泛型)

TestJson.java

@Data
@TableName(value = "test_json", autoResultMap = true)
public class TestJson {

    private Integer id;

    @TableField(typeHandler = JacksonTypeHandler.class)
    private Dog jsonInfo;

    @TableField(typeHandler = JacksonTypeHandler.class)
    private List<Dog> jsonInfoList;

    @Data
    public static class Dog {
        private String name;
    }

}

TestController.java

// 不使用 XxxMapper.xml 中的方法
TestJson testJson = testJsonMapper.selectOne(null);

因為在 XxxMapper.xml 中書寫 sql痘括,只能通過 resultMap 的方式指定 javaType 和 typeHandler(@TableField 注解指定的 typeHandler 不會被使用),但是卻無法將泛型信息傳遞給指定的 typeHandler滔吠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纲菌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子疮绷,更是在濱河造成了極大的恐慌翰舌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矗愧,死亡現(xiàn)場離奇詭異,居然都是意外死亡郑原,警方通過查閱死者的電腦和手機唉韭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犯犁,“玉大人属愤,你說我怎么就攤上這事∷嵋郏” “怎么了住诸?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵驾胆,是天一觀的道長。 經(jīng)常有香客問我贱呐,道長丧诺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任奄薇,我火速辦了婚禮驳阎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馁蒂。我一直安慰自己呵晚,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布沫屡。 她就那樣靜靜地躺著饵隙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沮脖。 梳的紋絲不亂的頭發(fā)上金矛,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音倘潜,去河邊找鬼绷柒。 笑死,一個胖子當著我的面吹牛涮因,可吹牛的內(nèi)容都是我干的废睦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼养泡,長吁一口氣:“原來是場噩夢啊……” “哼嗜湃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起澜掩,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤购披,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肩榕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刚陡,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年株汉,在試婚紗的時候發(fā)現(xiàn)自己被綠了筐乳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乔妈,死狀恐怖蝙云,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情路召,我是刑警寧澤勃刨,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布波材,位于F島的核電站,受9級特大地震影響身隐,放射性物質(zhì)發(fā)生泄漏廷区。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一抡医、第九天 我趴在偏房一處隱蔽的房頂上張望躲因。 院中可真熱鬧,春花似錦忌傻、人聲如沸大脉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镰矿。三九已至,卻和暖如春俘种,著一層夾襖步出監(jiān)牢的瞬間秤标,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工宙刘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苍姜,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓悬包,卻偏偏與公主長得像衙猪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子布近,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容