背景
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滔吠。