1 场景描述

postgresql数据库中常有表字段类型为jsonb的字段,在PO类映射的时候一般会采用java实体类的方式进行交互,使用mybatis进行操作的时候需要重写typeHandler。

2 handler类

2.1 JsonbTypeHandler

当jsonb字段值为对象时,即{“a”:1, “b”:2}这种。handler如下:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.postgresql.util.PGobject;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Jsonb转换器
 * mapper里json型字段到类的映射。
 * 用法一:
 * 入库:#{jsonDataField, typeHandler=com.adu.spring_test.mybatis.typehandler.JsonTypeHandler}
 * 出库:
 * <resultMap>
 * <result property="jsonDataField" column="json_data_field" javaType="com.xxx.MyClass" typeHandler="com.adu.spring_test.mybatis.typehandler.JsonTypeHandler"/>
 * </resultMap>
 * <p>
 * 用法二:
 * 1)在mybatis-config.xml中指定handler:
 *      <typeHandlers>
 *              <typeHandler handler="com.adu.spring_test.mybatis.typehandler.JsonTypeHandler" javaType="com.xxx.MyClass"/>
 *      </typeHandlers>
 * 2)在MyClassMapper.xml里直接select/update/insert。
 */
@MappedTypes({Object.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class JsonbTypeHandler extends AbstractJsonTypeHandler<Object> {

    private final Class<?> type;

    public JsonbTypeHandler(Class<?> type) {
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        if (ps != null) {
            PGobject jsonObject = new PGobject();
            jsonObject.setType("jsonb");
            jsonObject.setValue(JSON.toJSONString(parameter));
            ps.setObject(i, jsonObject);
        }
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object v=rs.getObject(columnName);
        return toFill(v);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object v=rs.getObject(columnIndex);
        return toFill(v);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object v=cs.getObject(columnIndex);
        return toFill(v);
    }
    @Override
    protected Object parse(String json) {
        return JSON.parseObject(json, this.type);
    }
    //必须将v转成 PGObject处理
    private Object toFill(Object v){
        if(v instanceof PGobject){
            PGobject p=(PGobject)v;
            String pv=p.getValue();
            if(StringUtils.isNotEmpty(pv)&&(p.getType().equals("jsonb")||p.getType().equals("json"))) {
                return parse(p.getValue());
            }
        }
        return v;
    }
    @Override
    protected String toJson(Object obj) {
        return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);
    }

}

2.2 JSONListTypeHandler

当数据库字段类型为列表时,即[{“a”: 1}, {“a”:2}]这种,handler如下:

import com.alibaba.fastjson.JSON;


public class Json2ListTypeHandler extends JsonbTypeHandler {

    /**
     * 泛型类型
     */
    private final Class<?> genericType;

    public Json2ListTypeHandler(Class<?> type) {
        super(type);
        this.genericType = type;
    }

    @Override
    protected Object parse(String json) {
        // 重写反序列化方法 默认转list 使用泛型
        return JSON.parseArray(json, this.genericType);
    }

    @Override
    protected String toJson(Object obj) {
        return super.toJson(obj);
    }

}

3 使用方法

3.1 PO类

@TypeDefs({@TypeDef(name = "JsonbTypeHandler", typeClass = JsonbTypeHandler.class),
        @TypeDef(name = "Json2ListTypeHandler", typeClass = Json2ListTypeHandler.class)})
public class Demo {
    
    @Column(columnDefinition = "jsonb")
    @Type(type = "JsonbTypeHandler")
    private A data1;

    
    @Column(columnDefinition = "jsonb")
    @Type(type = "Json2ListTypeHandler")
    private List<B> data2;
// 更新如果是mybatis-plus时可以不用@TypeDefs和@Column注解,这样框架有点混乱,在@TableName注解后面增加个autoResultMap = true即可,如
@TableName(value = "tbl_bigscreen_incident_safe_index_location", autoResultMap = true)
public class IncidentIndexLocationPo implements Serializable {

    private Integer id;

    /**
     * 模块名称
     */
    private String title;

    /**
     * 指标数组
     */
    @TableField(value = "indexs", typeHandler = JSONArrayHandler.class)
    private List<Integer> indexs;

    /**
     * 规则编码
     */
    @TableField(value = "incident_codes", typeHandler = JSONArrayHandler.class)
    private List<String> incidentCodes;
}

3.2 mapper.xml resultMap映射

<resultMap id="Demo" type="xxx.xxx.Demo">
        <id column="id" property="id" />

        <result column="a" property="a" typeHandler="xxx.xxx.JsonbTypeHandler" />
    </resultMap>
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐