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

使用Spring Data JDBC将Java Map持久化为PostgreSQL jsonb字段时读取实体报错,自定义ReadingConverter未生效的解决方法问询

解决Spring Data JDBC中Map<String, Int>与PostgreSQL JSONB列的读取转换问题

我太懂你遇到的这个坑了!Spring Data JDBC在处理简单类型的Map/集合时,逻辑有点“轴”——它会默认把这类容器当成需要关联查询的实体集合,完全跳过我们自定义的转换器,所以才会抛出找不到Integer类型持久化实体的异常,而你的读转换器根本没机会被调用。

问题根源拆解

Spring Data JDBC对Map、Collection这类容器类型有特殊判断:如果容器里的元素是Int这种简单类型,它会误以为这是需要通过外键关联的实体集合,直接去生成关联查询的SQL,完全不会触发我们配置的类型转换器。这就是写操作正常、读操作炸锅的核心原因。

正确的转换器修复方案

要让Spring Data JDBC乖乖用我们的转换器,得调整转换器的实现细节,同时确保它能正确对接PostgreSQL的JSONB类型处理逻辑:

调整后的完整配置代码

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import org.postgresql.util.PGobject
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.convert.converter.Converter
import org.springframework.data.convert.ReadingConverter
import org.springframework.data.convert.WritingConverter
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions
import org.springframework.data.jdbc.core.convert.JdbcValue
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories
import java.sql.JDBCType

@Configuration
@EnableJdbcRepositories
class DataConfig : AbstractJdbcConfiguration() {

    @Autowired
    private lateinit var objectMapper: ObjectMapper

    @Bean
    override fun jdbcCustomConversions(): JdbcCustomConversions {
        val converters = listOf(
            DistributionWritingConverter(objectMapper),
            DistributionReadingConverter(objectMapper)
        )
        return JdbcCustomConversions(converters)
    }

    @WritingConverter
    class DistributionWritingConverter(private val objectMapper: ObjectMapper) : Converter<Map<String, Int>, JdbcValue> {
        override fun convert(source: Map<String, Int>): JdbcValue {
            // 直接封装成PGobject并指定jsonb类型,贴合PostgreSQL的处理逻辑
            val pgObject = PGobject()
            pgObject.type = "jsonb"
            pgObject.value = objectMapper.writeValueAsString(source)
            return JdbcValue.of(pgObject, JDBCType.OTHER)
        }
    }

    @ReadingConverter
    class DistributionReadingConverter(private val objectMapper: ObjectMapper) : Converter<PGobject, Map<String, Int>> {
        override fun convert(source: PGobject): Map<String, Int> {
            return objectMapper.readValue(source.value, object : TypeReference<Map<String, Int>>() {})
        }
    }
}

关键优化点

  1. 写转换器优化:之前直接返回字符串并指定JDBCType.VARCHAR,现在改为封装成PGobject并明确标记jsonb类型,让PostgreSQL能正确识别数据类型,避免潜在的转换歧义。
  2. 确保读转换器被触发:你的读转换器类型Converter<PGobject, Map<String, Int>>本身是对的,因为PostgreSQL的JSONB列通过JDBC返回的就是PGobject实例。之前的异常是因为Spring Data JDBC没走到转换器这一步,而是直接去尝试关联查询,调整写转换器的逻辑后,框架会正确识别这是一个需要类型转换的字段,而非关联实体。

为什么包装类方案能生效

你后来用Distribution类包装Map<String, Int>的临时方案确实好用,因为Spring Data JDBC会把自定义类当成嵌入式对象(embeddable),而非关联实体集合,这时候就会触发类型转换器来处理字段转换,不会再去生成错误的关联查询SQL。这算是一个简单粗暴但有效的workaround。

额外注意事项

  • 确保你的ObjectMapper已经正确配置了Kotlin支持(比如添加jackson-module-kotlin依赖),避免序列化/反序列化时出现Jackson相关的异常。
  • 检查Spring Data JDBC和PostgreSQL JDBC驱动的版本兼容性,旧版本可能存在类似的类型处理bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 07:57:37