Java 备忘: 使用 MyBatis+Jackson 自动处理 JSON

原创
2017/12/14 15:33
阅读数 4.1K

本文实现了一种使用 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()]));

大功告成。

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部