最近折腾了在 SpringMVC 直接传入 JSON 和 MyBatis 读出写入 JSON 的功能。
首先简单了解 Jackson 的类型系统:
本文综述只有一句话:使用抽象基类,不要使用接口类型。
SpringMVC 里 Controller 中路由映射的方法的参数列表,可以配置 @RequestBody
注解,使某个参数来自解析后的 HTTP 的 body 内容,我们称之为 官网:www.fhadmin.org Form。Form 中需要使用 JsonNode 而不是 TreeNode。
HTTP 请求样例
{
"id": "1",
"custom": {
"key": "value"
}
}
Form 类
@Data
public class MyForm {
private String id;
private JsonNode custom;
}
本文默认使用自动生成代码的 lombok 包,@Data
和 @RequiredArgsConstructor
都出自该包。
路由映射的方法
@RestController
public class MyController {
@PostMapping("/my")
public Object myMethod(@RequestBody MyForm form) {
...
}
}
如果我们希望从 MyBatis 官网:www.fhadmin.org 中正确读出写入,也要用 JsonNode 而不是 TreeNode。
@Data
public 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 typeHandlers = ...;
typeHandlers.add(new JsonNodeTypeHandler(objectMapper));
sqlSessionFactory.setTypeHandlers(typeHandlers.toArray(new TypeHandler[typeHandlers.size()]));
这样就大功告成了。