TypeHandler 的应用之 JSON 处理
背景介绍
在实际开发中,我们经常会遇到需要将对象序列化为 JSON 存储到数据库,或者从数据库读取 JSON 字符串反序列化为对象的需求。MyBatis-Plus 提供了 AbstractJsonTypeHandler 来方便自定义 TypeHandler 以支持 JSON 处理。本文将介绍如何通过继承 AbstractJsonTypeHandler 自定义 TypeHandler 来实现 JSON 数据的处理。
核心接口和抽象类
IJsonTypeHandler 接口
MyBatis-Plus 提供了IJsonTypeHandler接口,它定义了两个核心方法:
public interface IJsonTypeHandler<T> {
T parse(String json); // 将 JSON 字符串解析为对象
String toJson(T obj); // 将对象转换为 JSON 字符串
}
AbstractJsonTypeHandler 抽象类
AbstractJsonTypeHandler是一个实现了IJsonTypeHandler接口的抽象类,它继承自 MyBatis 的BaseTypeHandler。这个抽象类主要提供了以下主要功能:
- 处理了 null 值的情况
- 支持通过字段类型进行初始化
public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> implements IJsonTypeHandler<T> {
protected final Log log = LogFactory.getLog(this.getClass());
protected final Class<?> type;
/**
* @since 3.5.6
*/
protected Type genericType;
/**
* 默认初始化
*
* @param type 类型
*/
public AbstractJsonTypeHandler(Class<?> type) {
this.type = type;
if (log.isTraceEnabled()) {
log.trace(this.getClass().getSimpleName() + "(" + type + ")");
}
Assert.notNull(type, "Type argument cannot be null");
}
/**
* 通过字段初始化
*
* @param type 类型
* @param field 字段
* @since 3.5.6
*/
public AbstractJsonTypeHandler(Class<?> type, Field field) {
this(type);
this.genericType = field.getGenericType();
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, toJson(parameter));
}
/**
* 帮子类处理了 null 值的情况
*/
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
final String json = rs.getString(columnName);
return StringUtils.isBlank(json) ? null : parse(json);
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
final String json = rs.getString(columnIndex);
return StringUtils.isBlank(json) ? null : parse(json);
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
final String json = cs.getString(columnIndex);
return StringUtils.isBlank(json) ? null : parse(json);
}
public Type getFieldType() {
return this.genericType != null ? this.genericType : this.type;
}
}
实际应用示例
下面通过一个支付渠道配置的例子(来自 ruoyi-vue-pro 的支付模块)来展示如何继承 AbstractJsonTypeHandler 来处理 JSON:
1. 定义 TypeHandler
public static class PayClientConfigTypeHandler extends AbstractJsonTypeHandler<Object> {
public PayClientConfigTypeHandler(Class<?> type) {
super(type);
}
public PayClientConfigTypeHandler(Class<?> type, Field field) {
super(type, field);
}
@Override
public Object parse(String json) {
PayClientConfig config = JsonUtils.parseObjectQuietly(json, new TypeReference<PayClientConfig>() {});
if (config != null) {
return config;
}
// 兼容老版本的包路径
String className = JsonUtils.parseObject(json, "@class", String.class);
className = StrUtil.subAfter(className, ".", true);
switch (className) {
case "AlipayPayClientConfig":
return JsonUtils.parseObject2(json, AlipayPayClientConfig.class);
case "WxPayClientConfig":
return JsonUtils.parseObject2(json, WxPayClientConfig.class);
case "NonePayClientConfig":
return JsonUtils.parseObject2(json, NonePayClientConfig.class);
default:
throw new IllegalArgumentException("未知的 PayClientConfig 类型:" + json);
}
}
@Override
public String toJson(Object obj) {
return JsonUtils.toJsonString(obj);
}
}
可以看到子类继承 AbstractJsonTypeHandler,只需要考虑序列化及反序列化,即实现 toJson 和 parse 方法。
另:这里 AlipayPayClientConfig 等实现了 PayClientConfig 接口,PayClientConfig 接口上添加了
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS),序列化时会增加 @class 属性保存实现类全路径:@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) // @JsonTypeInfo 注解的作用,Jackson 多态 // 1. 序列化到时数据库时,增加 @class 属性。 // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 @JsonIgnoreProperties(ignoreUnknown = true) // 目的:忽略未知的属性,避免反序列化失败 public interface PayClientConfig { /** * 参数校验 * * @param validator 校验对象 */ void validate(Validator validator); }parse 方法先从该属性获取到类名,再进行反序列化。
2. 在实体类中使用
@TableName(value = "pay_channel", autoResultMap = true)
public class PayChannelDO extends TenantBaseDO {
@TableField(typeHandler = PayClientConfigTypeHandler.class)
private PayClientConfig config;
}
注意需要将 @TableName autoResultMap 设置为 true(开启映射注解)。
总结
- 自定义 TypeHandler 继承
AbstractJsonTypeHandler类,实现parse和toJson方法 - 在实体类中使用
@TableField注解指定 TypeHandler,同时需要将 @TableName autoResultMap 设置为 true
实际上 MyBatis-Plus 官方提供了 JacksonTypeHandler 等实现,来处理 JSON 数据,没有自定义需求时直接使用即可。
参考:字段类型处理器
浙公网安备 33010602011771号