首页 > 解决方案 > spring-data-neo4j v6:找不到能够从 [MyDTO] 类型转换为 [org.neo4j.driver.Value] 类型的转换器

问题描述

情况

我正在将 kotlin spring data neo4j 应用程序从spring-data-neo4jversion迁移5.2.0.RELEASE到 version 6.0.11

原始应用程序有几个带有自定义查询的 Repository 接口,这些接口将一些 DTO 作为参数,并使用各种 DTO 字段来构造查询。所有这些类型的查询目前都失败了

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [MyDTO] to type [org.neo4j.driver.Value]

spring-data-neo4j v6的参考文档仅提供了传递给@Repository接口的自定义查询方法的参数与与该存储库关联的类具有相同类型的示例@Node。文档没有明确说明只允许使用 Node 类的参数。

问题

有什么方法可以将任意 DTO(不是类)传递给spring-data-neo4j v6@Node中的接口中的自定义查询方法,就像在 v5 中一样?@Repository

代码示例

示例节点实体

@Node
data class MyEntity(
    @Id
    val attr1: String,
    val attr2: String,
    val attr3: String
)

示例 DTO

data class MyDTO(
    val field1: String,
    val field2: String
)

示例存储库接口

@Repository
interface MyRepository : PagingAndSortingRepository<MyEntity, String> {

    // ConverterNotFoundException is thrown when this method is called
    @Query("MATCH (e:MyEntity {attr1: {0}.field1}) " +
           "CREATE (e)-[l:LINK]->(n:OtherEntity {attr2: {0}.field2))")
    fun doSomethingWithDto(dto: MyDTO)
}

到目前为止尝试的解决方案

将 DTO 注释为 Node 实体

基于参考文档https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#custom-queries.parameters中找到的以下内容

作为参数传递给使用自定义查询注释的函数的映射实体(带有@Node 的所有内容)将被转换为嵌套映射。

@Node
data class MyDTO(
    @Id
    val field1: String,
    val field2: String
)

在自定义查询中替换{0}$0

基于参考文档https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#custom-queries.parameters中找到的以下内容

您执行此操作的方式与在 Neo4j 浏览器或 Cypher-Shell 中发出的标准 Cypher 查询完全相同,使用 $ 语法(从 Neo4j 4.0 开始,Cypher 参数的旧 {foo} 语法已从数据库中删除)。

...

[在给定的清单中] 我们通过其名称引用参数。您也可以改用 $0 等。

@Repository
interface MyRepository : PagingAndSortingRepository<MyEntity, String> {
    
    // ConverterNotFoundException is thrown when this method is called
    @Query("MATCH (e:MyEntity {attr1: $0.field1}) " +
           "CREATE (e)-[l:LINK]->(n:OtherEntity {attr2: $0.field2))")
    fun doSomethingWithDto(dto: MyDTO)
}

细节

标签: springkotlinneo4jspring-data-neo4j

解决方案


RTFM自定义转换...

自己找到了解决方案。希望其他人也可以从中受益。

解决方案

创建自定义转换器

import mypackage.model.*
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.neo4j.driver.Value
import org.neo4j.driver.Values
import org.springframework.core.convert.TypeDescriptor
import org.springframework.core.convert.converter.GenericConverter
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
import java.util.HashSet

class DtoToNeo4jValueConverter : GenericConverter {
    override fun getConvertibleTypes(): Set<ConvertiblePair>? {
        val convertiblePairs: MutableSet<ConvertiblePair> = HashSet()
        convertiblePairs.add(ConvertiblePair(MyDTO::class.java, Value::class.java))
        return convertiblePairs
    }

    override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor?): Any? {
        return if (MyDTO::class.java.isAssignableFrom(sourceType.type)) {
            // generic way of converting an object into a map
            val dataclassAsMap = jacksonObjectMapper().convertValue(source as MyDTO, object :
                    TypeReference<Map<String, Any>>() {})
            Values.value(dataclassAsMap)
        } else null
    }
}

在配置中注册自定义转换器

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.neo4j.core.convert.Neo4jConversions
import org.springframework.core.convert.converter.GenericConverter
import java.util.*


@Configuration
class MyNeo4jConfig {
    @Bean
    override fun neo4jConversions(): Neo4jConversions? {
        val additionalConverters: Set<GenericConverter?> = Collections.singleton(DtoToNeo4jValueConverter())
        return Neo4jConversions(additionalConverters)
    }
}

推荐阅读