在 MyBatis 中编写 Neo4j 查询时,#{} 和 ${} 的主要区别在于参数替换的方式,这与 MyBatis 操作传统关系型数据库时的规则类似,但在 Neo4j 的 Cypher 查询中有一些特殊注意事项。
必须使用 ${} 而不能用 #{} 的情况:
-
动态标识符(节点标签、关系类型、属性名):
- 当需要动态指定节点标签、关系类型或属性名时,必须使用
${},因为这些位置 Cypher 语法不接受参数化。 - 例如:
这里MATCH (n:${label}) WHERE n.${property} = #{value} RETURN nlabel和property必须用${},而value可以用#{}。
- 当需要动态指定节点标签、关系类型或属性名时,必须使用
-
Cypher 关键字或子句动态拼接:
- 如果需要动态拼接 Cypher 的关键字部分(如 ORDER BY、WHERE 条件等),必须使用
${}。 - 例如:
MATCH (n:Person) ${whereClause} RETURN n
- 如果需要动态拼接 Cypher 的关键字部分(如 ORDER BY、WHERE 条件等),必须使用
-
动态索引或约束名称:
- 创建或使用索引/约束时,名称需要用
${}。
- 创建或使用索引/约束时,名称需要用
-
Cypher 函数或特殊语法:
- 如果参数需要作为 Cypher 函数名或特殊语法的一部分。
应该使用 #{} 的情况:
- 对于普通的属性值、查询参数等,应该始终优先使用
#{},因为:- 它会被预编译为参数化查询,防止 Cypher 注入
- 有类型安全处理
- 例如:
MATCH (n:Person) WHERE n.name = #{name} RETURN n
安全注意事项:
- 永远不要用
${}直接接收用户输入,这会导致 Cypher 注入风险 - 如果必须使用
${},应该:- 仅在动态 SQL 的必要部分使用
- 确保值来自可信来源(如系统常量、枚举值等)
- 考虑在应用层做白名单校验
示例对比:
<!-- 安全的方式 -->
<select id="findByLabelAndProperty">
MATCH (n:${label}) WHERE n.${property} = #{value} RETURN n
</select>
<!-- 危险的方式(不要这样用) -->
<select id="findByLabelUnsafe">
MATCH (n:${userInput}) RETURN n <!-- 可能被注入 -->
</select>
总结:在 Neo4j 查询中,只有当需要动态改变 Cypher 查询的结构部分(标签、属性名、关系类型等)时才必须用 ${},其他所有值都应该用 #{}。