Java 備忘: 使用 MyBatis+Jackson 自動處理 JSON 原

本文實現了一種使用 MyBatis 庫直接從數據庫讀入寫出 JSON 字符串,轉化爲 Jackson 庫的 JsonNode 的方法。

首先簡單瞭解 Jackson 的類型系統:

  1. TreeNode 接口是根類型
  2. JsonNode 抽象類是 TreeNode 接口的直接實現,也是其他 Node 的基類
  3. 之上再分爲 ValueNode 值節點和 ContainerNode 容器節點兩種
  4. 最後是廣爲人知的 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()]));

大功告成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章