在 Scala 中构建 SQL 普通查询的参数绑定或字符串插值?

问题描述 投票:0回答:1
def insertToTableSQLQuery(tableName: String, columnDefinitions: Seq[ColumnDefinition], rowData: ListBuffer[Any]): Future[Int] = db.run {
        val columns = columnDefinitions.map(_.name).mkString(", ")

        sqlu"insert into #$tableName(#$columns) values (#${rowData(0)}, #${rowData(1)}, #${rowData(2)}, #${rowData(3)}, #${rowData(4)});"
}

如何构建 sql 查询,以便使用 Scala 在 Slick 中动态绑定或插入 rowData 值?

def insertToTableSQLQuery(tableName: String, columnDefinitions: Seq[ColumnDefinition], rowData: ListBuffer[Any]): Future[Int] = db.run {
            val columnNames = columnDefinitions.map(_.name).mkString(", ")
            val placeholders = List.fill(rowData.length)("?").mkString(", ")

            sqlu"insert into #$tableName(#$columnNames) values (#${rowData.map(_ => "?").mkString(", ")});".bind(rowData: _*)
}

我尝试使用参数绑定,但这给我带来了错误。

错误

value bind is not a member of slick.sql.SqlAction[Int,slick.dbio.NoStream,slick.dbio.Effect]    
[error] possible cause: maybe a semicolon is missing before `value bind`?
[error]                 .bind(rowData: _*)
[error]                  
sql scala slick
1个回答
0
投票

在运行时在plain sql中注入值有两种方法。

Slick 中的普通 SQL 查询是使用

sql
sqlu
tsql
插值器通过字符串插值构建的。它们可以通过从 Slick 配置文件中标准
api._
导入来获得:

import slick.jdbc.H2Profile.api._

注入到查询中的任何变量或表达式都会变成结果查询字符串中的绑定变量。它不会直接插入到查询字符串中,因此不存在 SQL 注入攻击的危险。您可以在这里看到它的使用:

def insert(c: Coffee): DBIO[Int] =
  sqlu"insert into coffees values (${c.name}, ${c.supID}, ${c.price}, ${c.sales}, ${c.total})"

此方法生成的 SQL 语句始终相同:

insert into coffees values (?, ?, ?, ?, ?)

虽然大多数参数应作为绑定变量插入 SQL 语句中,但有时您需要将文字值直接拼接到语句中,例如抽象表名或运行动态生成的 SQL 代码。为此,您可以在所有插值器中使用

#$
代替
$
,如以下代码所示:

val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOption

因此,对于表或列等文字值,您需要使用

#$
$
作为变量值。

在你的情况下,代码应该是这样的

def insertToTableSQLQuery(
  tableName: String,
  columnDefinitions: Seq[ColumnDefinition],
  rowData: ListBuffer[Any] // this will produce a compilation error
): Future[Int] = db.run {
  val columns = columnDefinitions.map(_.name).mkString(", ")

  sqlu"insert into #$tableName(#$columns) values (${rowData(0)}, ${rowData(1)}, ${rowData(2)}, ${rowData(3)}, ${rowData(4)});"
}

这还不足以解决问题,因为当编译代码时,你会得到以下错误

could not find implicit value for parameter e: slick.jdbc.SetParameter[Any]
    sqlu"insert into #$tableName(#$columns) values (${rowData(0)}, ${rowData(1)}, ${rowData(2)}, ${rowData(3)}, ${rowData(4)});"

这意味着,slick 不知道如何绑定

Any
类型的值。 Slick 为 Int、Boolean、String、Doubel、BigDecimal 等常见值提供隐式 SetParameter

解决这个问题的一种方法是创建自己的

implicit SetParameter[Any] = ???

。我建议在任何 scala 项目中尽可能避免使用 
Any
 ,除非你确实需要。

另一种选择是使用

case class

Tuple
 而不是 
List[Any]
。 case 类更容易使用,因为您可以为每个字段设置名称,而当您想要访问元组时,您需要知道值的位置。案例类也更容易重构。

© www.soinside.com 2019 - 2024. All rights reserved.