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

查看:131
本文介绍了到目前为止我还无法理解的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?什么是投影-方法~'返回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?

推荐答案

[更新] -添加了[c5>理解

  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. 调用后,它会根据投影返回List(String, Int)元组.

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.

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

到目前为止,我们已经了解了如何通过精巧的方式在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 _ 告诉光滑的(Int, String)投影必须如何映射到案例类.

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所需的信息.和unapply 如果您猜对了,则相反.

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提取器,可用于获取idname Bar的构建:

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的是 . 调用时,它返回ListBaz个对象:

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 (选项提升)- 处理可能不是的值.

  • 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理解的评论:

  • In response to the comment on how Slick uses for comprehensions:

    以某种方式,单子总是设法出现并要求 成为解释的一部分...

    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中,用于理解的是用于 方法(可能是嵌套的)方法调用:上面的代码 (或多或少)等于:

    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天全站免登陆