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.
Bây giờ, về <>
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 field1
và B
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 id
và name
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
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]
Để đố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
, flatMap
Cá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
, flatMap
và filter
. 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
, map
và filter
đượ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 filter
và map
. 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?
Câu trả lời hay. –
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
@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