Spring Boot升级后@CompositeProperty嵌套Map存储Neo4j报错咨询
Spring Boot 2.7升级后Neo4j @CompositeProperty嵌套Map存储问题解决方案
当你把Spring Boot从2.3.12升级到2.7.x,将Neo4j节点的@Properties替换为@CompositeProperty后,嵌套Map无法像之前那样自动展开存储,这是因为Spring Data Neo4j(SDN)6.x(对应Spring Boot 2.7)对@CompositeProperty的实现逻辑做了调整:
- 旧版
@Properties会递归遍历嵌套Map,将其展开为扁平的前缀式键值对(比如params.key2.mapKey1)存储。 - 新版
@CompositeProperty默认仅支持单层简单类型的Map,嵌套的Map会被当作整体对象处理,但Neo4j本身不支持将Map作为属性值存储,因此抛出Neo.ClientError.Statement.TypeError错误。
要兼容原有数据格式且无需迁移,你可以通过自定义属性转换器来恢复旧版的嵌套Map展开逻辑:
步骤1:实现嵌套Map的转换逻辑
创建一个自定义转换器,手动处理嵌套Map的扁平化与还原:
import org.springframework.data.neo4j.core.convert.Neo4jConversionService import org.springframework.data.neo4j.core.convert.Neo4jPersistentPropertyConverter import org.springframework.data.neo4j.core.mapping.Neo4jPersistentProperty class NestedMapCompositeConverter : Neo4jPersistentPropertyConverter<MutableMap<String, Any?>> { // 将嵌套Map扁平化为前缀式键值对,写入Neo4j override fun write(source: MutableMap<String, Any?>, sink: Neo4jConversionService.PropertyConverterSink) { flattenNestedMap(source, "params", sink) } // 从扁平的前缀式键值对还原为嵌套Map override fun read(source: Map<String, Any>, property: Neo4jPersistentProperty): MutableMap<String, Any?> { return unflattenToNestedMap(source, "params") } private fun flattenNestedMap(map: Map<String, Any?>, prefix: String, sink: Neo4jConversionService.PropertyConverterSink) { map.forEach { (key, value) -> val fullKey = "$prefix.$key" when (value) { is Map<*, *> -> flattenNestedMap(value as Map<String, Any?>, fullKey, sink) else -> sink.write(fullKey, value) } } } private fun unflattenToNestedMap(source: Map<String, Any>, prefix: String): MutableMap<String, Any?> { val result = mutableMapOf<String, Any?>() val prefixWithDot = "$prefix." source.forEach { (key, value) -> if (key.startsWith(prefixWithDot)) { val remainingKey = key.substring(prefixWithDot.length) val splitIndex = remainingKey.indexOf('.') if (splitIndex == -1) { // 无嵌套的键直接存入 result[remainingKey] = value } else { // 有嵌套的键,逐层构建Map val topLevelKey = remainingKey.substring(0, splitIndex) val nestedKey = remainingKey.substring(splitIndex + 1) val nestedMap = result.getOrDefault(topLevelKey, mutableMapOf<String, Any?>()) as MutableMap<String, Any?> nestedMap[nestedKey] = value result[topLevelKey] = nestedMap } } } return result } }
步骤2:注册转换器到Neo4j配置
创建Neo4j配置类,将自定义转换器注册到转换服务中:
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.neo4j.core.convert.Neo4jConversions @Configuration class CustomNeo4jConfig { @Bean fun neo4jConversions(): Neo4jConversions { val converters = mutableSetOf<Any>() converters.add(NestedMapCompositeConverter()) return Neo4jConversions(converters) } }
步骤3:关联转换器与属性
在你的Neo4j节点类中,为params属性指定自定义转换器:
@CompositeProperty(prefix = "params", converter = NestedMapCompositeConverter::class) var params: MutableMap<String, Any?> = mutableMapOf()
完成以上配置后,@CompositeProperty就会像旧版@Properties一样处理嵌套Map,自动展开存储为扁平的前缀式键值对,完全兼容原有数据,无需迁移。
内容的提问来源于stack exchange,提问作者Aslak
相关产品推荐
相关产品推荐

