SpringMVC 和 MyBatis 支持 Jackson 的经验谈

来源:互联网 发布:上海交通卡充值软件下载 编辑:程序博客网 时间:2024/06/06 13:15

最近折腾了在 SpringMVC 直接传入 JSON 和 MyBatis 读出写入 JSON 的功能。

首先简单了解 Jackson 的类型系统:

  1. TreeNode 接口是根类型
  2. JsonNode 抽象类是 TreeNode 接口的直接实现,也是其他 Node 的基类
  3. 之上再分为 ValueNode 值节点和 ContainerNode 容器节点两种
  4. 最后是广为人知的 BooleanNode / StringNode / ArrayNode / ObjectNode 等节点

本文综述只有一句话:使用抽象基类,不要使用接口类型。

SpringMVC 里 Controller 中路由映射的方法的参数列表,可以配置 @RequestBody 注解,使某个参数来自解析后的 HTTP 的 body 内容,我们称之为 官网:www.fhadmin.org  Form。Form 中需要使用 JsonNode 而不是 TreeNode。

HTTP 请求样例

{    "id": "1",    "custom": {        "key": "value"    }}

Form 类

@Datapublic class MyForm {    private String id;    private JsonNode custom;}

本文默认使用自动生成代码的 lombok 包,@Data 和 @RequiredArgsConstructor 都出自该包。

路由映射的方法

@RestControllerpublic class MyController {    @PostMapping("/my")    public Object myMethod(@RequestBody MyForm form) {        ...    }}

如果我们希望从 MyBatis 官网:www.fhadmin.org 中正确读出写入,也要用 JsonNode 而不是 TreeNode。

@Datapublic class User {    private String id;    private JsonNode custom;}

需要实现 JsonNodeTypeHandler 并注册到 MyBatis 的 SqlSessionFactory

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 官网:www.fhadmin.org    public void setNonNullParameter(PreparedStatement ps, int i, JsonNode parameter, JdbcType jdbcType) throws SQLException {        String json = parameter.toString();        ps.setString(i, json);    }    private JsonNode read(String json) {        try {            return objectMapper.readTree(json);        } catch (JsonParseException e) {            if (LOG.isWarnEnabled()) {                LOG.warn("JSON parse failed", e);            }            return null;        } catch (IOException e) {            // should not occur, no real i/o...            throw new IllegalArgumentException(e.getMessage(), e);        }    }    @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);    }}
List<TypeHandler> typeHandlers = ...;typeHandlers.add(new JsonNodeTypeHandler(objectMapper));sqlSessionFactory.setTypeHandlers(typeHandlers.toArray(new TypeHandler[typeHandlers.size()]));

这样就大功告成了。