You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis无法映射PostgreSQL JSONB至Map/JsonNode的问题解决

解决PostgreSQL JSONB字段映射为Map<String, Object>的问题

方法1:自定义MyBatis TypeHandler(推荐方案)

MyBatis默认无法直接识别PostgreSQL的JSONB类型,需要自定义类型处理器完成JSONB与Map的双向转换:

1.1 编写TypeHandler类

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.postgresql.util.PGobject;
import com.fasterxml.jackson.databind.ObjectMapper;

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

public class JsonbTypeHandler extends BaseTypeHandler<Map<String, Object>> {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final PGobject JSONB_OBJECT = new PGobject();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
        JSONB_OBJECT.setType("jsonb");
        JSONB_OBJECT.setValue(OBJECT_MAPPER.writeValueAsString(parameter));
        ps.setObject(i, JSONB_OBJECT);
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parseJson(rs.getString(columnName));
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parseJson(rs.getString(columnIndex));
    }

    @Override
    public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parseJson(cs.getString(columnIndex));
    }

    private Map<String, Object> parseJson(String json) throws SQLException {
        if (json == null || json.isBlank()) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, Map.class);
        } catch (Exception e) {
            throw new SQLException("JSONB转Map失败", e);
        }
    }
}

1.2 关联TypeHandler到字段

有两种方式绑定:

  • 方式A:在DTO字段上标注
@Data
@Accessors(chain = true)
public class UserHasAssetDTO {

    private Integer id;

    ...

    @ResultType(JsonbTypeHandler.class)
    private Map<String, Object> props;
}
  • 方式B:在Repository查询注解中指定
@Select("select id, ..., props from bla bla bla")
@Results({
    @Result(column = "props", property = "props", typeHandler = JsonbTypeHandler.class)
})
List<UserHasAssetDTO> getALLAssetsFilteredByDate(@Param("username") String username, @Param("startDate") String startDate, @Param("endDate") String endDate);

1.3 依赖检查

确保项目引入必要依赖:

<!-- PostgreSQL驱动 -->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.6.0</version>
</dependency>
<!-- Jackson JSON处理包 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

方法2:使用MyBatis-Plus内置TypeHandler(若用MyBatis-Plus)

如果项目基于MyBatis-Plus开发,可以直接用框架内置的类型处理器:

@Data
@Accessors(chain = true)
public class UserHasAssetDTO {

    private Integer id;

    ...

    @TableField(typeHandler = JacksonTypeHandler.class)
    private Map<String, Object> props;
}

方法3:手动转换(临时应急方案)

若不想编写TypeHandler,可临时通过SQL转换+Service层手动解析:

  1. 修改Repository查询SQL,将JSONB转为字符串:
@Select("select id, ..., props::text as props from bla bla bla")
List<UserHasAssetDTO> getALLAssetsFilteredByDate(@Param("username") String username, @Param("startDate") String startDate, @Param("endDate") String endDate);
  1. 临时将DTO的props字段改为String类型,在Service层转为Map:
ObjectMapper objectMapper = new ObjectMapper();
List<UserHasAssetDTO> dtoList = userHasAssetRepository.getALLAssetsFilteredByDate(username, startDate, endDate);
for (UserHasAssetDTO dto : dtoList) {
    String propsStr = dto.getProps();
    if (propsStr != null) {
        Map<String, Object> propsMap = objectMapper.readValue(propsStr, Map.class);
        // 可将DTO改为包含String和Map两种props字段,或新建VO返回
    }
}

注意事项

  • 确保PostgreSQL驱动版本不低于42.x,旧版本对JSONB的支持不完善
  • 自定义TypeHandler需被MyBatis扫描到,可通过配置typeHandlersPackage或添加@Component注解(Spring环境)

内容的提问来源于stack exchange,提问作者Kambei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:42:24