光滑:使用Column [Int]值的困难

Ale*_*her 1 database scala scalatra slick

我有一个跟随我最近问的另一个Slick问题(Slick table Query:Trouble with recognition value).请多多包涵!!我是数据库的新手,Slick在文档方面似乎特别差.无论如何,我有这张桌子:

object Users extends Table[(Int, String)]("Users") {

  def userId          = column[Int]("UserId", O.PrimaryKey, O.AutoInc)
  def userName        = column[String]("UserName")

  def * = userId ~ userName
}
Run Code Online (Sandbox Code Playgroud)

第一部分

我正在尝试使用此函数进行查询:

def findByQuery(where: List[(String, String)]) = SlickInit.dbSlave withSession {    

  val q = for {
    x <- Users if foo((x.userId, x.userName), where)
           } yield x
      q.firstOption.map { case(userId, userName) =>
                    User(userId, userName)}
   }
Run Code Online (Sandbox Code Playgroud)

其中"where"是搜索查询列表// ex.("userId","1"),("userName","Alex")"foo"是测试相等性的辅助函数.我遇到了类型错误.
x.userId的类型为Column [Int].如何将其作为Int来操纵?我试过铸造,例如:

foo(x.userId.asInstanceOf[Int]...)
Run Code Online (Sandbox Code Playgroud)

但我也遇到了麻烦.如何处理Slick返回类型?

第二部分 是否有人熟悉铸造功能:

def*= userId~userName <>(User,User.unapply _)

?我知道这个问题有一些很好的答案,最值得一提的是:到目前为止我无法理解的scala光滑方法和一个非常相似的问题:在SLICK中用伴随对象映射投影.但任何人都可以解释为什么编译器会响应

<> method overloaded 
Run Code Online (Sandbox Code Playgroud)

对于那么简单的代码行?

ped*_*rla 6

让我们从问题开始:

val q = for {
    x <- Users if foo((x.userId, x.userName), where)
} yield x
Run Code Online (Sandbox Code Playgroud)

请参阅Slick将Scala表达式转换为SQL.为了能够根据需要将条件转换为SQL语句,Slick需要使用一些特殊类型.这些类型的工作方式实际上是Slick执行的转换的一部分.

例如,当您编写List(1,2,3) filter { x => x == 2 }过滤器谓词时,将对列表中的每个元素执行谓词.但是Slick不能那样做!所以Query [ATable]过滤器{arow => arow.id === 2}实际上意味着"使用条件id = 2进行选择"(我在此处跳过详细信息).

我写了一个模拟你的foo函数,并要求Slick生成查询的SQL q:

select x2."UserId", x2."UserName" from "Users" x2 where false
Run Code Online (Sandbox Code Playgroud)

看到了false?那是因为fooScala评估为布尔值的简单谓词false.在Query中而不是列表中执行的类似谓词评估为在SQL生成中需要完成的操作的描述.比较filterList和Slick中的s 之间的差异:

List[A].filter(A => Boolean):List[A]
Query[E,U].filter[T](f: E => T)(implicit wt: CanBeQueryCondition[T]):Query[E,U]  
Run Code Online (Sandbox Code Playgroud)

List过滤器计算为As列表,而Query.filter计算为新Query!

现在,迈向解决方案的一步.

看来你想要的实际上in是SQL 的运算符.该in如果列表中的一个元素运算符返回true,例如:4 in (1,2,3,4)是真的.请注意,这(1,2,3,4)是一个SQL列表,而不是像Scala中的Tuple.

对于此in操作符,SQL运算符Slick使用运算符inSet.

现在是问题的第二部分.(我将where变量重命名为list,因为where是Slick方法)

你可以尝试:

val q = for {
  x <- Users if (x.userId,x.userName) inSet list
} yield x
Run Code Online (Sandbox Code Playgroud)

但那不会编译!那是因为SQL没有Scala那样的元组.在SQL中你不能这样做(1,"Alfred") in ((1,"Alfred"),(2, "Mary"))(记住,这(x,y,z)是列表的SQL语法,我在这里滥用语法只是为了表明它是无效的 - 那里有很多SQL的方言,有些可能支持它们元组和列表以类似的方式.)

一种可能的解决方案是仅使用userId字段:

val q = for {
  x <- Users if x.userId inSet list2
} yield x
Run Code Online (Sandbox Code Playgroud)

这会产生 select x2."UserId", x2."UserName" from "Users" x2 where x2."UserId" in (1, 2, 3)

但由于您明确使用用户ID和用户名,因此可以合理地假设用户ID不能唯一标识用户.所以,要修改我们可以连接两个值.当然,我们需要在列表中做同样的事情.

val list2 = list map { t => t._1 + t._2 }
val q2 = for {
  x <- Users if (x.userId.asColumnOf[String] ++ x.userName) inSet list2
} yield x
Run Code Online (Sandbox Code Playgroud)

查看生成的SQL:

select x2."UserId", x2."UserName" from "Users" x2 
where (cast(x2."UserId" as VARCHAR)||x2."UserName") in ('1a', '3c', '2b')
Run Code Online (Sandbox Code Playgroud)

见上文||?它是H2Db中使用的字符串连接运算符.H2Db是我用来运行你的例子的Slick驱动程序.此查询和其他查询可能会略有不同,具体取决于您使用的数据库.

希望它能说明光滑如何工作并解决您的问题.至少第一个.:)