本文實現了一種使用 MyBatis 庫直接從數據庫讀入寫出 JSON 字符串,轉化爲 Jackson 庫的 JsonNode 的方法。
首先簡單瞭解 Jackson 的類型系統:
- TreeNode 接口是根類型
- JsonNode 抽象類是 TreeNode 接口的直接實現,也是其他 Node 的基類
- 之上再分爲 ValueNode 值節點和 ContainerNode 容器節點兩種
- 最後是廣爲人知的 BooleanNode / StringNode / ArrayNode / ObjectNode 等節點
在本文撰寫前,MyBatis 3 仍不支持接口作爲 MappedType 參數,但 mybatis-3/pull/947 似乎修復了這個問題。但無論如何,本文所述的方案仍然行之有效。
首先我們定義 domain 類
@Data
public class User {
private String id;
private JsonNode custom;
}
本文使用自動生成代碼的 lombok 包,@Data
和 @RequiredArgsConstructor
都出自該包。
需要從 JSON 解析的字段聲明爲 JsonNode 類型。
然後,實現自定義的 JsonNodeTypeHandler。
import java.io.*;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@MappedTypes(JsonNode.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonNodeTypeHandler extends BaseTypeHandler<JsonNode> {
private final ObjectMapper objectMapper;
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JsonNode parameter, JdbcType jdbcType) throws SQLException {
String json = parameter.toString();
ps.setString(i, json);
}
@Override
public JsonNode getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return read(json);
}
@Override
public JsonNode getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
return read(json);
}
@Override
public JsonNode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
return read(json);
}
private JsonNode read(String json) {
try {
return objectMapper.readTree(json);
} catch (JsonParseException e) {
// logging!
return null;
} catch (IOException e) {
// should not occur, no real i/o...
throw new IllegalArgumentException(e.getMessage(), e);
}
}
}
將 JsonNodeTypeHandler 註冊到 MyBatis 的 SqlSessionFactory。
List<TypeHandler> typeHandlers = ...;
typeHandlers.add(new JsonNodeTypeHandler(objectMapper));
sqlSessionFactory.setTypeHandlers(typeHandlers.toArray(new TypeHandler[typeHandlers.size()]));
大功告成。