Spring Data Neo4j中@Query传入关系类型参数失败的问题
问题场景
原本以下代码可正常运行:
interface MyRepository : Neo4jRepository<Character?, Long?> { @Query("MATCH (c1:Character)-[:positive]->(c2:Character) WHERE c1.id = \$id RETURN c2") fun findRelations(relation: String, id: Long): List<Character> }
但想要将关系类型(比如示例中的positive)作为参数动态传入,修改后的代码执行时出现语法错误:
interface MyRepository : Neo4jRepository<Character?, Long?> { @Query("MATCH (c1:Character)-[:\$relation]->(c2:Character) WHERE c1.id = \$id RETURN c2") fun findRelations(relation: String, id: Long): List<Character> }
报错信息:
org.springframework.dao.InvalidDataAccessResourceUsageException:
Invalid input '$': expected "%", "(" or an identifier (line 1,
column 24 (offset: 23))\n"MATCH
(c1:Character)-[:$relation]->(c2:Character) WHERE c1.id = $id RETURN
c2"\n ^; Error code
'Neo.ClientError.Statement.SyntaxError
尝试按照提示改用%,调整代码后仍报错:
interface MyRepository : Neo4jRepository<Character?, Long?> { @Query("MATCH (c1:Character)-[:\\%relation]->(c2:Character) WHERE c1.id = \$id RETURN c2") fun findRelations(relation: String, id: Long): List<Character> }
对应的错误信息:
org.springframework.dao.InvalidDataAccessResourceUsageException:
Invalid input '': expected "%", "(" or an identifier (line 1,
column 25 (offset: 24))\n"MATCH
(c1:Character)-[:%relation]->(c2:Character) WHERE c1.id = $id RETURN
c2"\n ^; Error code
'Neo.ClientError.Statement.SyntaxError
原因与解决方案
核心原因
Cypher语法中,关系类型属于语法结构元素,不能像普通参数那样用$relation直接占位。Spring Data Neo4j需要通过SpEL表达式来动态拼接这类语法元素。
正确代码写法
使用Spring的SpEL表达式#{#relation}替换关系类型,同时建议用反引号包裹以兼容特殊字符并避免注入风险:
interface MyRepository : Neo4jRepository<Character?, Long?> { @Query("MATCH (c1:Character)-[:`#{#relation}`]->(c2:Character) WHERE c1.id = \$id RETURN c2") fun findRelations(relation: String, id: Long): List<Character> }
如果你的关系类型不包含特殊字符或空格,也可以省略反引号:
interface MyRepository : Neo4jRepository<Character?, Long?> { @Query("MATCH (c1:Character)-[:#{#relation}]->(c2:Character) WHERE c1.id = \$id RETURN c2") fun findRelations(relation: String, id: Long): List<Character> }
补充说明
- SpEL表达式
#{#paramName}会在查询执行前被Spring解析,将参数值直接替换到Cypher语句中,生成合法的语法结构。 - 反引号的作用是将关系类型作为标识符处理,即使关系类型包含空格、特殊字符也能正常识别,同时能有效防止Cypher注入攻击,推荐始终使用。
内容的提问来源于stack exchange,提问作者JasSy

