在Scala中使用参数绑定还是字符串插值来构建SQL Plain?

thtygnil  于 4个月前  发布在  Scala
关注(0)|答案(1)|浏览(50)
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)});"
}

字符串
如何使用Scala构建SQL查询,以便在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]

dwthyt8l

dwthyt8l1#

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

  • 滑线插值

Slick中的普通SQL查询是通过使用sqlsqlutsql插值器的字符串插值构建的。它们可以通过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 (?, ?, ?, ?, ?)

  • slick -拼接文字值

虽然大多数参数应该作为绑定变量插入到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 classTuple来代替List[Any]。case类更容易使用,因为你可以为每个字段设置一个名称,而当你想要访问元组时,你需要知道值的位置。case类也更容易重构。

相关问题