使用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>>() {}) } } }
关键优化点
- 写转换器优化:之前直接返回字符串并指定
JDBCType.VARCHAR,现在改为封装成PGobject并明确标记jsonb类型,让PostgreSQL能正确识别数据类型,避免潜在的转换歧义。 - 确保读转换器被触发:你的读转换器类型
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
相关产品推荐
相关产品推荐

