스칼라 슬릭 방법은 지금까지 이해할 수 없습니다


89

나는 Slick 작품과 그에 필요한 것을 이해하려고 노력합니다.

여기에 예가 있습니다.

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무엇입니까?

답변:


198

[업데이트] - 이해에 대한 설명 추가for

  1. *방법 :

    이렇게하면 기본 투영 이 반환됩니다 .

    ' 일반적으로 관심있는 모든 열 (또는 계산 된 값) '.

    테이블에는 여러 필드가있을 수 있습니다. 기본 프로젝션에 대한 하위 집합 만 필요합니다. 기본 프로젝션은 테이블의 유형 매개 변수와 일치해야합니다.

    한 번에 하나씩 가져 가자. <>물건이 없으면 *:

    // 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)
    

    이와 같은 테이블 정의 만 있으면 다음과 같은 쿼리를 만들 수 있습니다.

    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)] 이와 같은 간단한 쿼리 를위한 것 입니다.

    // 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무엇입니까? 그것은 Query투영과 함께 (String, Int)입니다. 호출 할 때, 그것은 반환 List(String, Int)투사에 따라 튜플.

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

    이 경우 이해 의 yield절 에서 원하는 투영을 정의했습니다 for.

  2. 이제 <>Bar.unapply.

    이것은 Mapped Projections 라는 것을 제공합니다 .

    지금까지 우리는 (또는 계산 된 값) 의 프로젝션 을 반환하는 쿼리를 Scala에서 얼마나 매끄럽게 표현할 수 있는지 살펴 보았습니다 . 따라서 이러한 쿼리 실행할 때 쿼리 의 결과 행Scala 튜플 이라고 생각해야합니다 . 튜플의 유형은 정의 된 프로젝션과 일치합니다 ( for이전 예제에서와 같이 기본 *프로젝션 에 의한 이해에 의해 ). 이것이 where is the type of and is the type of field1 ~ field2의 프로젝션을 반환하는 이유 입니다 .Projection2[A, B]Afield1Bfield2

    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를 이름이 지정된 필드가있는 개체가 아니라 일부 개체 로 생각하고 싶습니다 .

    (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)프로젝션 이 케이스 클래스에 매핑되어야 하는 방법을 매끄럽게 알려줍니다 .

    이것은 양방향 매핑입니다. Bar케이스 클래스 생성자이므로 (id: Int, name: String)에서 Bar. 그리고 unapply 당신이 그것을 짐작했다면, 그 반대입니다.

    어디 unapply에서 왔습니까? 이것은 어떤 보통의 경우 클래스에 사용할 수있는 표준 스칼라 방법입니다 - 단지 정의가 Bar있는 A에게 제공 Bar.unapply추출기 돌아 가야하는 데 사용할 수있는 idname이 있음 Bar으로 지어진 :

    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)]
    

    따라서 기본 프로젝션을 가장 많이 사용할 것으로 예상되는 케이스 클래스에 매핑 할 수 있습니다.

    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 _)
    }
    

    또는 쿼리별로 가질 수도 있습니다.

    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 A는 QueryA의 매핑 에 투영 Baz. 호출 할 때, 그것은 반환 ListBaz목적 :

     val result: List[Baz] = q1.list
    
  3. 마지막으로, 옆으로는 같은 .?이벤트의 옵션 리프팅 -하지 않을 수 있습니다 값을 처리하는 스칼라 방법.

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

    마무리하면 다음과 같은 원래 정의와 잘 작동합니다 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]
    
  4. Slick이 for이해력을 사용하는 방법에 대한 의견에 대한 답변 :

    어쨌든 모나드는 항상 나타나서 설명의 일부가 될 것을 요구합니다 ...

    이해력은 컬렉션에만 국한되지 않습니다. 모든 종류의 Monad 에서 사용할 수 있으며 컬렉션은 Scala에서 사용할 수있는 많은 종류의 모나드 유형 중 하나 일뿐입니다.

    그러나 컬렉션은 익숙하기 때문에 설명을위한 좋은 출발점이됩니다.

    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은 메서드 (중첩 된) 메서드 호출에 대한 구문 설탕입니다. 위 코드는 다음과 같습니다.

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

    기본적으로, 아무것도는 filter, map, flatMap 방법 (즉, 모나드 )를 사용할 수 있습니다 for대신에 이해 ns. 좋은 예는 Option monad 입니다. 다음은 동일한 for명령문이 모나드 List와 모두에서 작동 하는 이전 예제입니다 Option.

    // (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
    

    마지막 예에서 변환은 다음과 같습니다.

    // 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에서 쿼리는 모나 딕 입니다. map, flatMapfilter메서드 가있는 객체 일뿐 입니다. 따라서 for이해력 ( *방법 설명에 표시됨)은 다음과 같이 해석됩니다.

    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
    

    볼 수 있듯이 flatMap, mapfilter를 생성하는 데 사용 Query의 반복 변형에 의해 Query(Bars) 각각의 호출로 filter하고 map. 컬렉션의 경우 이러한 메서드는 실제로 컬렉션을 반복하고 필터링하지만 Slick에서는 SQL을 생성하는 데 사용됩니다. 자세한 내용은 여기를 참조하십시오. Scala Slick은 어떻게 Scala 코드를 JDBC로 변환합니까?


'1'설명 블록에서 : 'val q ='가 WrappingQuery인지 확실하지 않으며 코드를 읽는 동안 List <Projection2>처럼 보입니다. 어떻게 그것이 Query ..로 변환 될 수 있는가? (저는 그것이 어떻게 작동하는지 이해하기 위해 당신의 설명을 가지고 놀고 있습니다. 감사합니다!)
ses

@ses-이것에 대한 (약간 긴) 설명을 추가했습니다 ... 또한 stackoverflow.com/questions/13454347/monads-with-java-8/…을보십시오 .-거의 동일한 내용이라는 것을 깨달았습니다.
Faiz

신비한 컴파일 오류를 경험하는 사람들에게 foo.? Option [T] 열의 경우 읽기 어려운 유형 불일치가 발생합니다. 고마워, 파이즈!
sventechie

1
이것은 훌륭한 대답입니다 ... Slick 3.0
Ixx

6

아무도 대답하지 않았으므로 시작하는 데 도움이 될 수 있습니다. 나는 Slick을 잘 모릅니다.

로부터 슬릭 문서 :

해제 된 삽입 :

모든 테이블에는 기본 프로젝션을 포함하는 * 메서드가 필요합니다. 이것은 쿼리에서 행 (테이블 개체 형식)을 반환 할 때 반환되는 내용을 설명합니다. Slick의 * 투영은 데이터베이스의 투영과 일치하지 않아도됩니다. 새 열 (예 : 계산 된 값 포함)을 추가하거나 원하는대로 일부 열을 생략 할 수 있습니다. * 프로젝션에 해당하는 리프팅되지 않은 유형은 테이블에 유형 매개 변수로 제공됩니다. 단순하고 매핑되지 않은 테이블의 경우 단일 열 유형 또는 열 유형의 튜플이됩니다.

즉, slick은 데이터베이스에서 반환 된 행을 처리하는 방법을 알아야합니다. 정의한 방법은 파서 결합 자 함수를 사용하여 열 정의를 행에서 사용할 수있는 것으로 결합합니다.


ook. Projection은 열의 표현 일뿐입니다. 예 : final class Projection2 [T1, T2] (override val _1 : Column [T1], override val _2 : Column [T2])는 Projection [()으로 Tuple2 (_1, _2)를 확장합니다. T1, T2)] {..
ses

자 .. 왜 그럴까요? Bar에는 '미적용'방법이 있나요?
ses dec.

2
Aha ..-모든 케이스 클래스는 Product 특성을 구현하고 unapply는 Product의 메서드입니다. 마법.
ses
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.