MyBatis-Plus 实现PostgreSQL数据库jsonb类型的保存与查询
新建Jsonb处理类
方式 1
/**
 * PostgreSQL jsonb 数据处理器
 *
 * @author CoderKK
 * @date 2025/09/08
 */
@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.OTHER)//JSONB对应JdbcType.OTHER
public class JsonbTypeHandler extends BaseTypeHandler<Object> {
    private static final PGobject jsonObject = new PGobject();
    private static final String JSONB_STR = "jsonb";
    private static final String JSON_STR = "json";
    /**
     * 写数据库时,把Java对象转成JSONB类型
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
        if (preparedStatement != null) {
            jsonObject.setType(JSONB_STR);
            jsonObject.setValue(JSON.toJSONString(o));
            preparedStatement.setObject(i, jsonObject);
        }
    }
    @Override
    public Object getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return JSON.parse(resultSet.getString(s));
    }
    @Override
    public Object getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return JSON.parse(resultSet.getString(i));
    }
    @Override
    public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return JSON.parse(callableStatement.getString(i));
    }
}
方式 2
如果数据库字段是字符类型,可以直接使用Mybatis-Plus内置的JacksonTypeHandler,如果是像PostgreSQL数据库的JSONB类型,则需要自定义开发一个TypeHandler,可以继承JacksonTypeHandler,然后覆写父类的方法,代码如下:
/**
 * PostgreSQL数据库中的JSON、JSONB字段类型的处理程序
 *
 * @author CoderKK
 * @date 2025/09/08
 */
@MappedTypes({Object.class})
@MappedJdbcTypes(JdbcType.OTHER)// JSONB对应JdbcType.OTHER
public class JsonbTypeHandler extends JacksonTypeHandler {
    private static final PGobject jsonObject = new PGobject();
    private static final String JSONB = "jsonb";
    private static final String JSON = "json";
    public JsonbTypeHandler(Class<?> type) {
        super(type);
    }
    /**
     * 写数据库时,把java对象转成JSONB类型
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        if (ps != null) {
            jsonObject.setType(JSONB);
            jsonObject.setValue(toJson(parameter));
            ps.setObject(i, jsonObject);
        }
    }
    /**
     * 读数据时,把JSONB类型的字段转成java对象
     */
    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object v = rs.getObject(columnName);
        return convertDbToJavaObject(v);
    }
    /**
     * 读数据时,把JSONB类型的字段转成java对象
     */
    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object v = rs.getObject(columnIndex);
        return convertDbToJavaObject(v);
    }
    /**
     * 读数据时,把JSONB类型的字段转成java对象
     */
    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object v = cs.getObject(columnIndex);
        return convertDbToJavaObject(v);
    }
    /**
     * 读数据时,把JSONB类型的字段转成java对象
     */
    private Object convertDbToJavaObject(Object v) {
        if (Objects.isNull(v)) {
            return null;
        }
        if (!PGobject.class.isAssignableFrom(v.getClass())) {
            return v;
        }
        PGobject p = (PGobject) v;
        String type = p.getType();
        if (type == null) {
            return v;
        }
        if (!JSONB.equalsIgnoreCase(type) && !JSON.equalsIgnoreCase(type)) {
            return v;
        }
        String pv = p.getValue();
        if (StringUtils.isBlank(pv)) {
            return v;
        }
        try {
            return parse(pv);
        } catch (Exception e) {
            // 根据实际业务需求,可以选择返回null、抛出异常或返回原始值
            return v;
        }
    }
}
推荐方式 2 可以兼容所有类型自动转换,其他类型例如数组类型等都可以写自定义转换器实现
使用JacksonTypeHandler可以帮开发者自动处理json格式与java对象之间的映射关系,不需要每次手动做序列化和反序列化的工作了。
PostgreSQL 整数数组类型处理程序
/**
 * PostgreSQL 整数数组类型处理程序
 *
 * @author CoderKK
 * @date 2025/09/08
 */
public class IntegerArrayTypeHandler extends BaseTypeHandler<Integer[]> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Integer[] parameter, JdbcType jdbcType) throws SQLException {
        if (parameter != null && parameter.length > 0) {
            StringBuilder sb = new StringBuilder("{");
            for (int j = 0; j < parameter.length; j++) {
                sb.append(parameter[j]);
                if (j < parameter.length - 1) {
                    sb.append(",");
                }
            }
            sb.append("}");
            ps.setString(i, sb.toString());
        } else {
            ps.setNull(i, java.sql.Types.VARCHAR);
        }
    }
    @Override
    public Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String str = rs.getString(columnName);
        return parseString(str);
    }
    @Override
    public Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String str = rs.getString(columnIndex);
        return parseString(str);
    }
    @Override
    public Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String str = cs.getString(columnIndex);
        return parseString(str);
    }
    private Integer[] parseString(String str) {
        if (str == null || !str.startsWith("{") || !str.endsWith("}")) {
            return null;
        }
        // 去掉首尾的大括号
        String content = str.substring(1, str.length() - 1);
        if (content.isEmpty()) {
            return new Integer[0];
        }
        String[] parts = content.split(",");
        Integer[] result = new Integer[parts.length];
        for (int i = 0; i < parts.length; i++) {
            result[i] = Integer.parseInt(parts[i]);
        }
        return result;
    }
}
优化版IntegerArrayTypeHandler (推荐使用此方法)
/**
 * PostgreSQL 整数数组类型处理程序
 *
 * @author CoderKK
 * @date 2025/09/08
 */
@Slf4j
public class IntegerArrayTypeHandler extends BaseTypeHandler<Integer[]> {
    // PostgreSQL 数组常量定义
    private static final String EMPTY_ARRAY = "{}";
    private static final String ARRAY_START = "{";
    private static final String ARRAY_END = "}";
    private static final String DELIMITER = ",";
    /**
     * 设置非空参数到 PreparedStatement
     *
     * @param ps        PreparedStatement 对象
     * @param i         参数位置
     * @param parameter 要设置的 Integer 数组
     * @param jdbcType  JDBC 类型
     * @throws SQLException 如果数据库访问出错
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Integer[] parameter, JdbcType jdbcType) throws SQLException {
        // 将 Integer 数组转换为 PostgreSQL 数组字符串格式
        ps.setString(i, toPgArrayString(parameter));
    }
    /**
     * 从 ResultSet 中通过列名获取结果
     *
     * @param rs         ResultSet 对象
     * @param columnName 列名
     * @return 解析后的 Integer 数组,可能为 null
     * @throws SQLException 如果数据库访问出错
     */
    @Override
    public Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parsePgArray(rs.getString(columnName));
    }
    /**
     * 从 ResultSet 中通过列索引获取结果
     *
     * @param rs          ResultSet 对象
     * @param columnIndex 列索引
     * @return 解析后的 Integer 数组,可能为 null
     * @throws SQLException 如果数据库访问出错
     */
    @Override
    public Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parsePgArray(rs.getString(columnIndex));
    }
    /**
     * 从 CallableStatement 中获取结果
     *
     * @param cs          CallableStatement 对象
     * @param columnIndex 列索引
     * @return 解析后的 Integer 数组,可能为 null
     * @throws SQLException 如果数据库访问出错
     */
    @Override
    public Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parsePgArray(cs.getString(columnIndex));
    }
    /**
     * 将 Integer 数组转换为 PostgreSQL 数组字符串格式
     *
     * @param array 要转换的 Integer 数组
     * @return PostgreSQL 数组格式的字符串,空数组返回 "{}"
     */
    private String toPgArrayString(Integer[] array) {
        // 处理 null 或空数组
        if (array == null || array.length == 0) {
            return EMPTY_ARRAY;
        }
        // 使用流式处理过滤 null 元素并转换为字符串
        return Stream.of(array)
                .filter(Objects::nonNull)  // 过滤掉 null 元素
                .map(String::valueOf)     // 转换为字符串
                .collect(Collectors.joining(
                        DELIMITER,           // 分隔符
                        ARRAY_START,         // 前缀
                        ARRAY_END            // 后缀
                ));
    }
    /**
     * 解析 PostgreSQL 数组字符串为 Integer 数组
     *
     * @param pgArray PostgreSQL 数组格式的字符串
     * @return 解析后的 Integer 数组,输入为 null 时返回 null,空数组返回空数组
     * @throws IllegalArgumentException 如果数组格式无效或包含无法解析的元素
     */
    private Integer[] parsePgArray(String pgArray) {
        // 处理 null 输入
        if (pgArray == null) {
            return null;
        }
        // 处理空数组
        if (EMPTY_ARRAY.equals(pgArray)) {
            return new Integer[0];
        }
        // 验证数组格式是否正确
        if (!pgArray.startsWith(ARRAY_START) || !pgArray.endsWith(ARRAY_END)) {
            log.error("Invalid PostgreSQL array format: {}", pgArray);
            return null;
        }
        try {
            // 提取数组内容部分
            String content = pgArray.substring(1, pgArray.length() - 1);
            // 处理空内容情况
            if (content.isEmpty()) {
                return new Integer[0];
            }
            // 分割、清理并转换数组元素
            return Arrays.stream(content.split(DELIMITER))
                    .map(String::trim)           // 去除空格
                    .filter(s -> !s.isEmpty())   // 过滤空字符串
                    .map(Integer::valueOf)       // 转换为 Integer
                    .toArray(Integer[]::new);    // 收集为数组
        } catch (NumberFormatException e) {
            log.error("Error occurred while parsing PostgreSQL array: {}", pgArray, e);
        }
        return null;
    }
}
创建实体类
1️⃣设置 autoResultMap = true,自动为实体类生成一个结果映射
2️⃣字段上添加处理器
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
@TableName(value = "t_uz_word_assess", autoResultMap = true)
public class WordAssessPO implements Serializable {
    /**
     * 主键ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    /**
     * 定位点(x1,x2,y1,y2)
     */
    @TableField(value = "point", typeHandler = IntegerArrayTypeHandler.class)
    private Integer[] point;
    /**
     * 各维度得分
     */
    @TableField(value = "dim_score", typeHandler = JsonbTypeHandler.class)
    private EvalDim dimScore;
    /**
     * 创建时间
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}
原始Mybatis映射
    <resultMap id="ReportInfo" type="com.xx.entity.ReportInfo">
        <result property="currentHandler" column="current_handler" javaType="java.util.List" typeHandler="com.xx.utils.JSONTypePgHandler"></result>
    </resultMap>
本文来自博客园,作者:Micky233,转载请注明原文链接:https://www.cnblogs.com/geek233/p/19080222

                
            
        
浙公网安备 33010602011771号