到目前为止我无法理解的 scala slick 方法 [英] scala slick method I can not understand so far

查看:31
本文介绍了到目前为止我无法理解的 scala slick 方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试着去理解一些 Slick 的作品以及它需要什么.

I try to understand some Slick works and what it requires.

这是一个例子:

package models

case class Bar(id: Option[Int] = None, name: String)

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  // This is the primary key column
  def name = column[String]("name")

  // Every table needs a * projection with the same type as the table's type parameter
  def * = id.? ~ name <>(Bar, Bar.unapply _)
}

谁能解释一下这里*方法的目的是什么,什么是<>,为什么unapply?什么是 Projection - method ~' 返回 Projection2 的实例?

Could somebody explain me what's the purpose of * method here, what is <>, why unapply? and what is Projection - method ~' returns the instance of Projection2?

推荐答案

[UPDATE] - 添加(又一个)关于 for 理解的解释

[UPDATE] - added (yet another) explanation on for comprehensions

  1. * 方法:

这将返回默认投影 - 这就是您的描述:

This returns the default projection - which is how you describe:

通常感兴趣的所有列(或计算值).

'all the columns (or computed values) I am usually interested' in.

您的表格可以有多个字段;你只需要一个子集您的默认投影.默认投影必须与类型匹配表的参数.

Your table could have several fields; you only need a subset for your default projection. The default projection must match the type parameters of the table.

让我们一次一个.没有 <> 的东西,只有 *:

Let's take it one at a time. Without the <> stuff, just the *:

// First take: Only the Table Defintion, no case class:

object Bars extends Table[(Int, String)]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")

  def * = id ~ name // Note: Just a simple projection, not using .? etc
}

// Note that the case class 'Bar' is not to be found. This is 
// an example without it (with only the table definition)

这样的表定义就可以让您进行如下查询:

Just a table definition like that will let you make queries like:

implicit val session: Session = // ... a db session obtained from somewhere

// A simple select-all:
val result = Query(Bars).list   // result is a List[(Int, String)]

(Int, String) 的默认投影导致一个 List[(Int, String)]用于诸如此类的简单查询.

the default projection of (Int, String) leads to a List[(Int, String)] for simple queries such as these.

// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q = 
   for (b <- Bars if b.id === 42) 
     yield (b.name ~ 1)
     // yield (b.name, 1) // this is also allowed: 
                          // tuples are lifted to the equivalent projection.

q 的类型是什么?它是一个带有 (String, Int) 投影的 Query.调用时,它根据投影返回 (String, Int) 元组的 List.

What's the type of q? It is a Query with the projection (String, Int). When invoked, it returns a List of (String, Int) tuples as per the projection.

 val result: List[(String, Int)] = q.list

在本例中,您已经在 yield 子句中定义了所需的投影for 的理解.

In this case, you have defined the projection you want in the yield clause of the for comprehension.

现在关于 <>Bar.unapply.

这提供了所谓的映射投影.

到目前为止,我们已经看到了 slick 是如何让您在 Scala 中表达查询的返回列的投影(或计算值);所以当执行这些查询您必须将查询的结果行视为 Scala 元组.元组的类型将匹配定义的投影(由您的for 理解与前面的示例相同,默认为 * 投影).这就是为什么 field1 ~ field2 返回 Projection2[A, B] 的投影,其中Afield1的类型,Bfield2的类型.

So far we've seen how slick allows you to express queries in Scala that return a projection of columns (or computed values); So when executing these queries you have to think of the result row of a query as a Scala tuple. The type of the tuple will match the Projection that is defined (by your for comprehension as in the previous example, of by the default * projection). This is why field1 ~ field2 returns a projection of Projection2[A, B] where A is the type of field1 and B is the type of field2.

q.list.map {
  case (name, n) =>  // do something with name:String and n:Int
}

Queury(Bars).list.map {
  case (id, name) =>  // do something with id:Int and name:String 
}

我们正在处理元组,如果我们有太多的元组可能会很麻烦列.我们不希望将结果视为 TupleN 而是一些具有命名字段的对象.

We're dealing with tuples, which may be cumbersome if we have too many columns. We'd like to think of results not as TupleN but rather some object with named fields.

(id ~ name)  // A projection

// Assuming you have a Bar case class:
case class Bar(id: Int, name: String) // For now, using a plain Int instead
                                      // of Option[Int] - for simplicity

(id ~ name <> (Bar, Bar.unapply _)) // A MAPPED projection

// Which lets you do:
Query(Bars).list.map ( b.name ) 
// instead of
// Query(Bars).list.map { case (_, name) => name }

// Note that I use list.map instead of mapResult just for explanation's sake.

这是如何工作的?<> 需要一个投影 Projection2[Int, String] 和返回 Bar 类型的映射投影.两个参数 Bar, Bar.unapply _告诉 slick 这个 (Int, String) 投影必须如何映射到 case 类.

How does this work? <> takes a projection Projection2[Int, String] and returns a mapped projection on the type Bar. The two arguments Bar, Bar.unapply _ tell slick how this (Int, String) projection must be mapped to a case class.

这是一个双向映射;Bar 是 case 类的构造函数,所以这就是从 (id: Int, name: String)Bar 所需的信息.并且取消申请如果你已经猜到了,那就是相反的.

This is a two-way mapping; Bar is the case class constructor, so that's the information needed to go from (id: Int, name: String) to a Bar. And unapply if you've guessed it, is for the reverse.

unapply 从何而来?这是一个标准的 Scala 方法,可用于任何普通的案例类——只要定义 Bar 就会给你一个 Bar.unapply是一个 提取器,可用于取回 idnameBar 是用以下代码构建的:

Where does unapply come from? This is a standard Scala method available for any ordinary case class - just defining Bar gives you a Bar.unapply which is an extractor that can be used to get back the id and name that the Bar was built with:

val bar1 = Bar(1, "one")
// later
val Bar(id, name) = bar1  // id will be an Int bound to 1,
                          // name a String bound to "one"
// Or in pattern matching
val bars: List[Bar] = // gotten from somewhere
val barNames = bars.map {
  case Bar(_, name) => name
}

val x = Bar.unapply(bar1)  // x is an Option[(String, Int)]

因此您的默认投影可以映射到您最希望使用的案例类:

So your default projection can be mapped to the case class you most expect to use:

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def * = id ~ name <>(Bar, Bar.unapply _)
}

或者您甚至可以按查询使用它:

Or you can even have it per-query:

case class Baz(name: String, num: Int)

// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q1 = 
   for (b <- Bars if b.id === 42) 
     yield (b.name ~ 1 <> (Baz, Baz.unapply _))

这里q1的类型是一个Query,带有一个映射投影到Baz.调用时,它返回一个 Baz 对象的 List:

Here the type of q1 is a Query with a mapped projection to Baz. When invoked, it returns a List of Baz objects:

 val result: List[Baz] = q1.list

  • 最后,顺便说一句,.? 提供了 Option Lifting - Scala 方式处理可能不是的值.

  • Finally, as an aside, the .? offers Option Lifting - the Scala way of dealing with values that may not be.

     (id ~ name)   // Projection2[Int, String] // this is just for illustration
     (id.? ~ name) // Projection2[Option[Int], String]
    

    总结起来,这将与您对 Bar 的原始定义很好地配合:

    Which, wrapping up, will work nicely with your original definition of Bar:

    case class Bar(id: Option[Int] = None, name: String)
    
    // SELECT b.id, b.name FROM bars b WHERE b.id = 42;
    val q0 = 
       for (b <- Bars if b.id === 42) 
         yield (b.id.? ~ b.name <> (Bar, Bar.unapply _))
    
    
    q0.list // returns a List[Bar]
    

  • 回应关于 Slick 如何使用 for 推导式的评论:

    不知何故,单子总是设法出现并要求成为解释的一部分...

    Somehow, monads always manage to show up and demand to be part of the explanation...

    理解不仅限于集合.它们可以用于任何类型的 Monad,并且集合是这只是 Scala 中可用的多种 monad 类型中的一种.

    For comprehensions are not specific to collections only. They may be used on any kind of Monad, and collections are just one of the many kinds of monad types available in Scala.

    但由于集合很熟悉,它们是一个良好的开端点解释:

    But as collections are familiar, they make a good starting point for an explanation:

    val ns = 1 to 100 toList; // Lists for familiarity
    val result = 
      for { i <- ns if i*i % 2 == 0 } 
        yield (i*i)
    // result is a List[Int], List(4, 16, 36, ...)
    

    在 Scala 中,for comprehension 是语法糖方法(可能是嵌套的)方法调用:上面的代码是(或多或少)等价于:

    In Scala, a for comprehension is syntactic sugar for method (possibly nested) method calls: The above code is (more or less) equivalent to:

    ns.filter(i => i*i % 2 == 0).map(i => i*i)
    

    基本上,任何带有filtermapflatMap方法(换句话说,Monad)可以在for 理解代替 ns.一个很好的例子是 Option monad.这是前面的例子其中相同的 for 语句适用于List 以及 Option monads:

    Basically, anything with filter, map, flatMap methods (in other words, a Monad) can be used in a for comprehension in place of ns. A good example is the Option monad. Here's the previous example where the same for statement works on both the List as well as Option monads:

    // (1)
    val result = 
      for { 
        i <- ns          // ns is a List monad
        i2 <- Some(i*i)  // Some(i*i) is Option
          if i2 % 2 == 0 // filter
      } yield i2
    
    // Slightly more contrived example:
    def evenSqr(n: Int) = { // return the square of a number 
      val sqr = n*n         // only when the square is even
      if (sqr % 2 == 0) Some (sqr)
      else None
    }
    
    // (2)
    result = 
      for { 
        i <- ns  
        i2 <- evenSqr(i) // i2 may/maynot be defined for i!
      } yield i2
    

    在最后一个例子中,转换可能看起来像这样:

    In the last example, the transformation would perhaps look like this:

    // 1st example
    val result = 
      ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
    
    // Or for the 2nd example
    result = 
      ns.flatMap(i => evenSqr(i)) 
    

    在 Slick 中,查询是一元的——它们只是具有mapflatMapfilter 方法.所以 for 理解(显示在 * 方法的解释中)只是转换为:

    In Slick, queries are monadic - they are just objects with the map, flatMap and filter methods. So the for comprehension (shown in the explanation of the * method) just translates to:

    val q = 
      Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
    // Type of q is Query[(String, Int)]
    
    val r: List[(String, Int)] = q.list // Actually run the query
    

    如您所见,flatMapmapfilter 用于通过对Query(Bars)的重复变换生成一个Query每次调用 filtermap.如果是这些方法实际上是迭代和过滤集合但在 Slick 中,它们用于生成 SQL.更多细节在这里:Scala Slick 如何将 Scala 代码翻译成 JDBC?

    As you can see, flatMap, map and filter are used to generate a Query by the repeated transformation of Query(Bars) with each invocation of filter and map. In the case of collections these methods actually iterate and filter the collection but in Slick they are used to generate SQL. More details here: How does Scala Slick translate Scala code into JDBC?

    这篇关于到目前为止我无法理解的 scala slick 方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆