Các *
phương pháp:
Điều này trả về phép chiếu mặc định - đó là cách bạn mô tả:
'tất cả các cột (hoặc các giá trị được tính toán) mà tôi thường quan tâm'.
Bảng của bạn có thể có một số trường; bạn chỉ cần một tập hợ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 tham số kiểu của bảng.
Hãy làm từng cái một. Không có những <>
thứ, chỉ có *
:
// 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ỉ 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)]
phép chiếu mặc định của các (Int, String)
khách hàng tiềm năng đến một List[(Int, String)]
cho các truy vấn đơn giản như sau.
// 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 của là q
gì? Nó là một Query
với hình chiếu (String, Int)
. Khi gọi, nó sẽ trả về một List
trong (String, Int)
các bộ theo dự báo.
val result: List[(String, Int)] = q.list
Trong trường hợp này, bạn đã xác định phép chiếu mà bạn muốn trong yield
mệnh đề của phần for
hiểu.
Bây giờ về <>
và Bar.unapply
.
Điều này cung cấp cái được gọi là Phép chiếu được Ánh xạ .
Cho đến nay, chúng ta đã thấy cách slick cho phép bạn thể hiện các truy vấn trong Scala trả về hình chiếu các cột (hoặc các giá trị được tính toán); Vì vậy, khi thực hiện các truy vấn này, bạn phải nghĩ về hàng kết quả của một truy vấn là một bộ Scala . Loại bộ tuple sẽ khớp với Phép chiếu được xác định (theo cách for
hiểu của bạn
như trong ví dụ trước, theo *
phép chiếu mặc định ). Đây là lý do tại sao field1 ~ field2
trả về một phép chiếu về Projection2[A, B]
đâu
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ộ giá trị, 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ĩ đến kết quả không phải là TupleN
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.
Cái này hoạt động ra sao? <>
lấy một phép chiếu Projection2[Int, String]
và trả về một phép chiếu được ánh xạ trên kiểu Bar
. Hai đối số Bar, Bar.unapply _
cho biết rõ ràng cách này(Int, String)
phép chiếu phải được ánh xạ tới một lớp trường hợp.
Đây là một ánh xạ hai chiều; Bar
là hàm tạo lớp case, vì vậy đó là thông tin cần thiết để chuyển từ (id: Int, name: String)
a Bar
. Và unapply
nếu bạn đã đoán ra, thì ngược lại.
Từ đâu unapply
đến? Đây là một phương pháp Scala tiêu chuẩn có sẵn cho bất kỳ lớp trường hợp thông thường nào - chỉ cần xác định Bar
sẽ cung cấp cho bạn một Bar.unapply
phương thức giải nén có thể được sử dụng để lấy lại id
và name
cái
Bar
được xây dựng bằng:
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, phép chiếu mặc định của bạn có thể được ánh xạ tới lớp trường hợp mà bạn mong muốn sử dụng nhất:
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 bạn thậm chí có thể có nó cho 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 loại q1
là a Query
với một phép chiếu được ánh xạ tới Baz
. Khi được gọi, nó trả về một List
trong Baz
các đối tượng:
val result: List[Baz] = q1.list
Cuối cùng, bên cạnh đó, .?
cung cấp tùy chọn Nâng - cách Scala xử lý các giá trị có thể không.
(id ~ name) // Projection2[Int, String] // this is just for illustration
(id.? ~ name) // Projection2[Option[Int], String]
Điều này, kết thúc, sẽ hoạt động tốt với định nghĩa ban đầu của bạn về 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]
Trả lời nhận xét về cách Slick sử dụng các từ for
hiểu:
Bằng cách nào đó, các monads luôn cố gắng xuất hiện và yêu cầu trở thành một phần của lời giải thích ...
Đối với sự hiểu biết 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 nguyên nào , và các bộ sưu tập chỉ là một trong nhiều loại đơn nguyên có sẵn trong Scala.
Nhưng vì các bộ sưu tập đã quen thuộc, chúng là một điểm khởi đầu tốt để 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 để hiểu là đường cú pháp cho các lệnh gọi phương thức (có thể lồng nhau): Đoạn mã trên (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
phương pháp (hay nói cách khác, một đơn nguyên ) có thể được sử dụng trong một
for
sự hiểu biết thay ns
. Một ví dụ điển hình là đơn nguyên Option . Đây là ví dụ trước đó trong đó for
câu lệnh tương tự hoạt động trên cả
monads List
cũng như 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
Trong ví dụ cuối cùng, phép biến đổi có thể 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 là monadic - họ chỉ là đối tượng với map
, flatMap
và filter
phương pháp. Vì vậy, sự for
hiểu biết (được hiển thị trong phần giải thích của *
phương pháp) chỉ chuyển thành:
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 có thể thấy, flatMap
, map
và filter
được sử dụng để tạo ra một Query
bởi sự biến đổi lặp lại Query(Bars)
với nhau gọi trình filter
và map
. Trong trường hợp tập hợp, các phương thức này thực sự lặp lại và lọc tập hợp nhưng trong Slick, chúng được sử dụng để tạo SQL. Thông tin chi tiết tại đây:
Scala Slick dịch mã Scala sang JDBC như thế nào?