使用 spring 数据 jdbc 坚持 Java 映射为 PostgreSQL jsonb
Persist Java Map as PostgreSQL jsonb using spring data jdbc
我正在使用 spring-data-jdbc 将以下实体保存到 PostgreSQL
@Table("abc_configurations")
data class AbcConfiguration(
val name: String,
val distribution: Map<String, Int>
)
分布是 PostgreSQL 中的一个 jsonb 列 table:
CREATE TABLE abc_configurations
(
name VARCHAR(50) PRIMARY KEY,
distribution JSONB NOT NULL
);
我为此创建了以下 custom converter:
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 {
return JdbcValue.of(objectMapper.writeValueAsString(source), JDBCType.VARCHAR)
}
}
@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>>() {})
}
}
}
当我持久化一个实体时,它工作正常,并且分布 Map
按预期持久化到 JSONB 列中。
问题是当我读取实体时,出现以下错误:Couldn't find PersistentEntity for type class java.lang.Integer!
org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.Integer!
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:79) ~[spring-data-commons-2.4.5.jar:2.4.5]
at org.springframework.data.jdbc.core.convert.SqlGeneratorSource.lambda$getSqlGenerator[=16=](SqlGeneratorSource.java:62) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330) ~[na:na]
at org.springframework.data.jdbc.core.convert.SqlGeneratorSource.getSqlGenerator(SqlGeneratorSource.java:61) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.sql(DefaultDataAccessStrategy.java:583) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.findAllByPath(DefaultDataAccessStrategy.java:362) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.4.jar:5.3.4]
我尝试将 @ReadingConverter
方法更改为以下方法,但没有用:
Converter<String, Map<String, Int>>
Converter<JdbcValue, Map<String, Int>>
未使用 ReadingConverter。
如何告诉 spring 使用自定义 @ReadingConverter
更新 (01.09.2021):
正如 Jen 和 Susan 所提到的,这是 spring-data-jdbc 库的一个问题。我继续进行以下工作。
data class Distribution(val value: Map<String, Int>)
@Table("abc_configurations")
data class AbcConfiguration(
val name: String,
val distribution: Distribution
)
地图确实在 Spring 数据 JDBC 中得到特殊处理,因此您不能简单地为它们使用转换器。
而是将地图包装在自定义类型中并为该类型注册转换器。
我正在使用 spring-data-jdbc 将以下实体保存到 PostgreSQL
@Table("abc_configurations")
data class AbcConfiguration(
val name: String,
val distribution: Map<String, Int>
)
分布是 PostgreSQL 中的一个 jsonb 列 table:
CREATE TABLE abc_configurations
(
name VARCHAR(50) PRIMARY KEY,
distribution JSONB NOT NULL
);
我为此创建了以下 custom converter:
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 {
return JdbcValue.of(objectMapper.writeValueAsString(source), JDBCType.VARCHAR)
}
}
@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>>() {})
}
}
}
当我持久化一个实体时,它工作正常,并且分布 Map
按预期持久化到 JSONB 列中。
问题是当我读取实体时,出现以下错误:Couldn't find PersistentEntity for type class java.lang.Integer!
org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.Integer!
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:79) ~[spring-data-commons-2.4.5.jar:2.4.5]
at org.springframework.data.jdbc.core.convert.SqlGeneratorSource.lambda$getSqlGenerator[=16=](SqlGeneratorSource.java:62) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330) ~[na:na]
at org.springframework.data.jdbc.core.convert.SqlGeneratorSource.getSqlGenerator(SqlGeneratorSource.java:61) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.sql(DefaultDataAccessStrategy.java:583) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy.findAllByPath(DefaultDataAccessStrategy.java:362) ~[spring-data-jdbc-2.1.5.jar:2.1.5]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.4.jar:5.3.4]
我尝试将 @ReadingConverter
方法更改为以下方法,但没有用:
Converter<String, Map<String, Int>>
Converter<JdbcValue, Map<String, Int>>
未使用 ReadingConverter。
如何告诉 spring 使用自定义 @ReadingConverter
更新 (01.09.2021):
正如 Jen 和 Susan 所提到的,这是 spring-data-jdbc 库的一个问题。我继续进行以下工作。
data class Distribution(val value: Map<String, Int>)
@Table("abc_configurations")
data class AbcConfiguration(
val name: String,
val distribution: Distribution
)
地图确实在 Spring 数据 JDBC 中得到特殊处理,因此您不能简单地为它们使用转换器。 而是将地图包装在自定义类型中并为该类型注册转换器。