2012-12-16 23 views
85

Tôi cố gắng hiểu một số công trình Slick và những gì nó đòi hỏi.phương pháp scala slick Tôi không thể hiểu cho đến nay

Ở đây nó một ví dụ:

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

ai đó có thể giải thích cho tôi mục đích của phương pháp * đây là những gì, là những gì <>, tại sao unapply? và Dự đoán - phương pháp ~ 'trả về phiên bản của Projection2 là gì?

Trả lời

189

[UPDATE]-thêm (nhưng khác) giải thích về for comprehensions

  1. Phương pháp *:

    này trả về chiếu mặc định - đó là cách bạn mô tả:

    'tất cả các cột (hoặc giá trị được tính) Tôi là thường là quan tâm' in.

    Bảng của bạn có thể có nhiều trường; bạn chỉ cần một tập con cho phép chiếu mặc định của mình. Phép chiếu mặc định phải khớp với các thông số loại của bảng.

    Hãy xem từng mã một.Nếu không có những thứ <>, chỉ là *:

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

    Chỉ cần một định nghĩa bảng như vậy sẽ cho phép bạn thực hiện các truy vấn như:

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

    chiếu mặc định của (Int, String) dẫn đến một List[(Int, String)] cho các truy vấn đơn giản như vậy như thế này

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

    Loại q là gì? Nó là Query với phép chiếu (String, Int). Khi được gọi, nó trả về một List trong số (String, Int) bộ dữ liệu theo dự báo.

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

    Trong trường hợp này, bạn đã xác định chiếu bạn muốn trong yield khoản của for hiểu.

  2. Bây giờ, về <>Bar.unapply.

    Điều này cung cấp những gì được gọi là Phép chiếu được ánh xạ.

    Cho đến nay, chúng tôi đã thấy mức độ khéo léo cho phép bạn thể hiện các truy vấn trong Scala trả lại phép chiếu của các cột (hoặc giá trị được tính); Vì vậy, khi thực hiện các truy vấn này bạn phải suy nghĩ về hàng kết quả của truy vấn làm bộ tảo Scala. Loại tuple sẽ khớp với Phép chiếu được xác định (bằng cách hiển thị số for của bạn như trong ví dụ trước, theo mặc định là * phép chiếu). Đây là lý do tại sao field1 ~ field2 trả về phép chiếu Projection2[A, B] trong đó A là loại field1B là loại 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 
    } 
    

    Chúng tôi đang xử lý các bộ dữ liệu, có thể cồng kềnh nếu chúng tôi có quá nhiều cột . Chúng tôi muốn nghĩ kết quả không phải là TupleN mà là một số đối tượng có các trường được đặt tên.

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

    Tính năng này hoạt động như thế nào? <> có một phép chiếu Projection2[Int, String] và trả về một phép chiếu được ánh xạ trên loại Bar. Hai đối số Bar, Bar.unapply _ cho biết mức độ khéo léo về cách chiếu này (Int, String) phải được ánh xạ tới một lớp chữ.

    Đây là bản đồ hai chiều; Bar là trình tạo lớp mẫu, do đó, đó là thông tin cần thiết để đi từ (id: Int, name: String) đến Bar. Và unapply nếu bạn đã đoán nó, là để đảo ngược.

    Nơi nào unapply xuất phát từ đâu?Đây là một Scala phương pháp tiêu chuẩn có sẵn cho bất kỳ trường hợp lớp bình thường - chỉ cần xác định Bar mang đến cho bạn một Bar.unapply mà là một vắt có thể được sử dụng để lấy lại idname rằng Bar được xây dựng với:

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

    Vì vậy, dự báo mặc định của bạn có thể được ánh xạ tới các lớp trường hợp bạn nhất mong đợi để sử dụng:

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

    Hoặc thậm chí bạn có thể có nó mỗi truy vấn:

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

    Đây là loại q1 là một Query với một ánh xạ chiếu để Baz. Khi gọi, nó sẽ trả về một List của Baz đối tượng:

    val result: List[Baz] = q1.list 
    
  3. Cuối cùng, như một sang một bên, .? cung cấp Tùy chọn nâng - cách Scala của đối phó với giá trị mà có thể không.

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

    nào, kết thúc, sẽ làm việc độc đáo với định nghĩa ban đầu của bạn của 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. Để đối phó với những nhận xét về cách Slick sử dụng for comprehensions:

    Bằng cách nào đó, monads luôn quản lý để hiển thị và yêu cầu là một phần của giải thích ...

    Để hiểu được không chỉ dành riêng cho các bộ sưu tập. Chúng có thể được sử dụng trên bất kỳ loại nào của Monad và các bộ sưu tập là chỉ là một trong nhiều loại đơn nguyên có sẵn trong Scala.

    Nhưng như các bộ sưu tập đã quen thuộc, họ làm cho một tốt bắt đầu điểm cho một lời giải thích:

    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, ...) 
    

    Trong Scala, một cho hiểu là cú pháp đường cho phương pháp phương pháp (có thể lồng nhau) gọi: Đoạn mã trên là (nhiều hơn hoặc ít hơn) tương đương với:

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

    Về cơ bản, bất cứ điều gì với filter, map, flatMapCác phương thức(nói cách khác, một Monad) có thể được sử dụng trong số for hiểu tại vị trí ns. Một ví dụ tốt là là Option monad.Dưới đây là ví dụ trước nơi tuyên bố cùng for hoạt động trên cả List cũng như 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 
    

    Trong ví dụ trước, việc chuyển đổi sẽ có lẽ trông như thế này:

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

    Trong Slick, truy vấn đơn điệu - chúng chỉ là các đối tượng có các phương thức các phương pháp map, flatMapfilter. Vì vậy, các for hiểu (hiển thị trong lời giải thích của phương pháp *) chỉ dịch để:

    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 
    

    Như bạn thấy, flatMap, mapfilter được sử dụng để tạo ra một Query bởi việc chuyển đổi lặp đi lặp lại của Query(Bars) với mỗi yêu cầu filtermap. Trong trường hợp của , các bộ sưu tập này thực sự lặp lại và lọc bộ sưu tập nhưng trong Slick chúng được sử dụng để tạo ra SQL. Chi tiết tại đây: How does Scala Slick translate Scala code into JDBC?

+11

Câu trả lời hay. –

+0

Trong khối giải thích '1': Không rõ ràng rằng 'val q =' là WrappingQuery, nó trông giống như một Danh sách trong khi đọc mã. Làm thế nào là nó có thể biến đổi thành truy vấn ..? (Tôi vẫn đang chơi với những lời giải thích của bạn để hiểu cách nó hoạt động. Cảm ơn bạn vì điều này!) – ses

+0

@ses - thêm một lời giải thích (hơi dài) về điều này ... Ngoài ra, hãy xem http://stackoverflow.com/questions/13454347/monads-with-java-8/13455602 # 13455602 - Tôi nhận ra rằng đó là nội dung gần như giống nhau. – Faiz

6

Vì không có ai khác trả lời, điều này có thể giúp bạn bắt đầu. Tôi không biết Slick rất tốt.

Từ Slick documentation:

Lifted Nhúng:

Mỗi bảng đòi hỏi một phương pháp * contatining một chiếu mặc định. Điều này mô tả những gì bạn nhận được khi bạn trả lại hàng (dưới dạng một đối tượng bảng ) từ truy vấn. Phép chiếu * của Slick không cần phải khớp với phép chiếu trong cơ sở dữ liệu. Bạn có thể thêm cột mới (ví dụ: với giá trị được tính) hoặc bỏ qua một số cột tùy thích. Loại không được nâng lên tương ứng với phép chiếu * được đưa ra dưới dạng tham số kiểu thành Bảng. Đối với các bảng đơn giản, không được ánh xạ, đây sẽ là một cột đơn lẻ hoặc một bộ các loại cột.

Nói cách khác, cần phải biết cách xử lý hàng được trả về từ cơ sở dữ liệu. Phương thức bạn đã xác định sử dụng các hàm kết hợp trình phân tích cú pháp của chúng để kết hợp các định nghĩa cột của bạn thành một cái gì đó có thể được sử dụng trên một hàng.

+0

ook. và chiếu chỉ là trình bày các cột .. như: lớp thức Projection2 [T1, T2] ( override val _1: Cột [T1], override val _2: Cột [T2] ) kéo dài Tuple2 (_1, _2) với chiếu [(T1, T2)] {.. – ses

+0

Bây giờ .. làm thế nào đến đó: Bar có phương pháp 'unapply'? – ses

+2

Aha .. - tất cả các lớp chữ thường triển khai đặc điểm Sản phẩm và không áp dụng là phương pháp của Sản phẩm. Ma thuật. – ses

Các vấn đề liên quan