将模式名称作为变量传递给查询的最佳方法
Posted
技术标签:
【中文标题】将模式名称作为变量传递给查询的最佳方法【英文标题】:Best way to pass the schema name as a variable to a query 【发布时间】:2015-03-13 08:50:48 【问题描述】:我有一个 PlayFramework 服务器(使用 Anorm),它针对具有多个架构的数据库进行操作,所有架构都具有相同的表。
我的大部分“访问数据库”函数看起来像:
def findById(zoneName: String, id: Long): Option[Employee] =
DB.withConnection implicit connection =>
SQL("""select *
from """+zoneName+"""employee
where employee._id = id"""
.on(
'_id -> id
).as(simpleParser.singleOpt)
但我知道这是一种错误的方法,因为它不是 SQL 注入安全的,而且在每个函数中编写当然很乏味。
我想使用字符串插值来纠正这个问题,它适用于我的 id
变量,但不适用于 zoneName
:
def findById(zoneName: String, id: Long): Option[Employee] =
DB.withConnection implicit connection =>
SQL"""select *
from $zoneName.employee
where employee._id = 1"""
.as(simpleParser.singleOpt)
给我:
info] ! @6lenhal6c - Internal server error, for (GET) [/limbo/br/employee/1] ->
[info]
[info] play.api.Application$$anon$1: Execution exception[[PSQLException: ERROR: syntax error at or near «$1»
[info] Position: 25]]
[info] at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.8.jar:2.3.8]
[info] at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.8.jar:2.3.8]
[info] at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.8.jar:2.3.8]
[info] at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.8.jar:2.3.8]
[info] at scala.Option.map(Option.scala:146) [scala-library-2.11.5.jar:na]
[info] Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near «$1»
[info] Position: 25
[info] at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198) ~[postgresql-9.3-1102.jdbc4.jar:na]
[info] at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1927) ~[postgresql-9.3-1102.jdbc4.jar:na]
[info] at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255) ~[postgresql-9.3-1102.jdbc4.jar:na]
[info] at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:561) ~[postgresql-9.3-1102.jdbc4.jar:na
也使用$zoneName
进行了测试,结果相同。
任何有关如何编写此内容的帮助或建议将不胜感激,在此先感谢您!
【问题讨论】:
您可以设置search_path
以包含您在调用函数之前“当前”处理的架构。或者,如果您使用不同的数据库用户,则将该用户的默认 search_path
设置为该用户应使用的架构名称。
我考虑过这个选项,但我放弃了它,因为将来我需要支持超过 1 个模式的查询
那么我认为除了在运行时连接正确的模式之外,您别无选择。老实说,这个设置看起来确实有点奇怪。您试图通过在不同模式中使用“相同”表来解决什么问题?也许有更好的方法来解决那个
也许有更好的解决方案,是的。我所做的是让几家公司(或同一家公司的分支机构)使用同一个数据库。我的服务器是访问这些数据的 RESTful 提供程序。通过这种方式,我将数据从一家公司保存到另一家公司,但我保留了进行暗示多个模式的查询的可能性。正如您所建议的,也许一种方法是让 db 用户使用 search_path 并使用正确的用户进行查询?还是保留分开的数据库?
你提前知道哪些查询会是跨模式的吗?如果这样做,您可以在“默认”模式中创建视图,以结合来自其他模式的信息。这样,您可以更改请求中的 search_path
以匹配当前租户,而不必担心在单个查询中访问来自不同架构的对象。
【参考方案1】:
使用 Anorm 字符串插值,任何$expression
都将被提供一个参数,也就是说,如果它是一个字符串,它将被 JDBC 驱动程序引用。
如果您想用字符串(例如动态模式)替换部分 SQL 语句,您可以使用连接,或者从最新版本(2.4.0-M3 或 2.3.8)开始使用语法 #$expr
。
val table = "myTable"
SQL"SELECT * FROM #$table WHERE id=$id"
// SELECT * FROM myTable WHERE id=?
【讨论】:
我将测试#$expr 方式,我不知道这种语法,看起来不错。你知道这是否只是替换字符串?在这种情况下,它不会解决我的 SQL 注入问题。我在哪里可以找到这方面的文档? 这种语法只允许将标准字符串插值与异常参数插值混合。标准字符串插值不会阻止 SQL 注入。以上是关于将模式名称作为变量传递给查询的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
将带有变量的闭包传递给 Laravel 查询构建器中的 where 方法
如何使用 SQLAlchemy 将查询结果传递给变量(作为字符串)? [复制]
PHP MVC 最佳实践 - 将 Session 变量从控制器传递给模型类或直接在模型中访问