The *
Metode:
Ini mengembalikan proyeksi default - seperti yang Anda gambarkan:
'semua kolom (atau nilai yang dihitung) yang biasanya saya minati'.
Tabel Anda dapat memiliki beberapa bidang; Anda hanya memerlukan subset untuk proyeksi default Anda. Proyeksi default harus sesuai dengan jenis parameter tabel.
Mari kita lakukan satu per satu. Tanpa <>
barang, hanya *
:
object Bars extends Table[(Int, String)]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id ~ name
}
Hanya definisi tabel seperti itu yang akan memungkinkan Anda membuat kueri seperti:
implicit val session: Session =
val result = Query(Bars).list
proyeksi default dari (Int, String)
prospek ke List[(Int, String)]
untuk kueri sederhana seperti ini.
val q =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1)
Apa jenisnya q
? Ini adalah Query
dengan proyeksi (String, Int)
. Saat dipanggil, ia mengembalikan a List
dari (String, Int)
tupel sesuai proyeksi.
val result: List[(String, Int)] = q.list
Dalam hal ini, Anda telah menentukan proyeksi yang Anda inginkan dalam yield
klausa for
pemahaman.
Sekarang tentang <>
dan Bar.unapply
.
Ini memberikan apa yang disebut Proyeksi yang Dipetakan .
Sejauh ini kita telah melihat bagaimana slick memungkinkan Anda untuk mengekspresikan kueri di Scala yang mengembalikan proyeksi kolom (atau nilai yang dihitung); Jadi saat menjalankan kueri ini, Anda harus memikirkan baris hasil kueri sebagai tupel Scala . Jenis tupel akan cocok dengan Proyeksi yang ditentukan (oleh for
pemahaman Anda
seperti pada contoh sebelumnya, dari *
proyeksi default ). Inilah sebabnya mengapa field1 ~ field2
mengembalikan proyeksi di Projection2[A, B]
mana
A
jenis field1
dan B
jenisnya field2
.
q.list.map {
case (name, n) =>
}
Queury(Bars).list.map {
case (id, name) =>
}
Kita berurusan dengan tupel, yang mungkin merepotkan jika kita memiliki terlalu banyak kolom. Kami ingin menganggap hasil bukan sebagai TupleN
objek dengan bidang bernama.
(id ~ name)
case class Bar(id: Int, name: String) // For now, using a plain Int instead
(id ~ name <> (Bar, Bar.unapply _))
Query(Bars).list.map ( b.name )
Bagaimana cara kerjanya? <>
mengambil proyeksi Projection2[Int, String]
dan mengembalikan proyeksi yang dipetakan pada tipe Bar
. Kedua argumen tersebut Bar, Bar.unapply _
menjelaskan secara apik bagaimana (Int, String)
proyeksi ini harus dipetakan ke kelas kasus.
Ini adalah pemetaan dua arah; Bar
adalah konstruktor kelas kasus, jadi itulah informasi yang diperlukan untuk beralih dari (id: Int, name: String)
ke a Bar
. Dan unapply
jika Anda sudah dapat menebaknya, itu sebaliknya.
Dari mana unapply
asalnya Ini adalah metode Scala standar yang tersedia untuk semua kelas kasus biasa - hanya dengan mendefinisikan yang Bar
memberi Anda Bar.unapply
sebuah ekstraktor yang dapat digunakan untuk mendapatkan kembali id
dan name
yang
Bar
telah dibangun dengan:
val bar1 = Bar(1, "one")
val Bar(id, name) = bar1
val bars: List[Bar] =
val barNames = bars.map {
case Bar(_, name) => name
}
val x = Bar.unapply(bar1)
Jadi proyeksi default Anda dapat dipetakan ke kelas kasus yang paling ingin Anda gunakan:
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 _)
}
Atau Anda bahkan dapat memilikinya per kueri:
case class Baz(name: String, num: Int)
val q1 =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1 <> (Baz, Baz.unapply _))
Di sini tipe q1
adalah a Query
dengan proyeksi yang dipetakan ke Baz
. Saat dipanggil, ini mengembalikan a List
dari Baz
objek:
val result: List[Baz] = q1.list
Terakhir, sebagai tambahan, .?
penawaran Option Lifting - cara Scala menangani nilai-nilai yang mungkin tidak.
(id ~ name)
(id.? ~ name)
Yang mana, sebagai penutup, akan bekerja dengan baik dengan definisi asli Anda tentang Bar
:
case class Bar(id: Option[Int] = None, name: String)
val q0 =
for (b <- Bars if b.id === 42)
yield (b.id.? ~ b.name <> (Bar, Bar.unapply _))
q0.list
Menanggapi komentar tentang bagaimana Slick menggunakan for
pemahaman:
Entah bagaimana, monad selalu muncul dan menuntut untuk menjadi bagian dari penjelasan ...
Untuk pemahaman tidak khusus untuk koleksi saja. Mereka dapat digunakan pada semua jenis Monad , dan koleksinya hanyalah salah satu dari banyak jenis monad yang tersedia di Scala.
Tetapi karena koleksi sudah familiar, mereka menjadi titik awal yang baik untuk penjelasan:
val ns = 1 to 100 toList;
val result =
for { i <- ns if i*i % 2 == 0 }
yield (i*i)
Dalam Scala, pemahaman for adalah gula sintaksis untuk panggilan metode (mungkin bersarang): Kode di atas (lebih atau kurang) setara dengan:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
Pada dasarnya, apa-apa dengan filter
, map
, flatMap
metode (dengan kata lain, sebuah Monad ) dapat digunakan dalam
for
pemahaman di tempat ns
. Contoh yang bagus adalah Option monad . Berikut contoh sebelumnya di mana sama for
pernyataan bekerja pada kedua
List
serta Option
monads:
val result =
for {
i <- ns
i2 <- Some(i*i)
if i2 % 2 == 0
} yield i2
def evenSqr(n: Int) = {
val sqr = n*n
if (sqr % 2 == 0) Some (sqr)
else None
}
result =
for {
i <- ns
i2 <- evenSqr(i)
} yield i2
Pada contoh terakhir, transformasi mungkin akan terlihat seperti ini:
val result =
ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
result =
ns.flatMap(i => evenSqr(i))
Di Slick, kueri bersifat monadik - mereka hanyalah objek dengan metode map
, flatMap
dan filter
. Jadi for
pemahaman (ditunjukkan dalam penjelasan *
metode) hanya diterjemahkan menjadi:
val q =
Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
val r: List[(String, Int)] = q.list
Seperti yang Anda lihat, flatMap
, map
dan filter
digunakan untuk menghasilkan Query
oleh transformasi berulang Query(Bars)
dengan setiap permintaan dari filter
dan map
. Dalam kasus koleksi, metode ini sebenarnya mengulangi dan memfilter koleksi tetapi di Slick mereka digunakan untuk menghasilkan SQL. Lebih jelasnya di sini:
Bagaimana Scala Slick menerjemahkan kode Scala ke JDBC?
Karena tidak ada orang lain yang menjawab, ini mungkin membantu Anda memulai. Saya tidak terlalu mengenal Slick.
Dari dokumentasi Slick :
Dengan kata lain, slick perlu mengetahui cara menangani baris yang dikembalikan dari database. Metode yang Anda tentukan menggunakan fungsi kombinator parsernya untuk menggabungkan definisi kolom Anda menjadi sesuatu yang dapat digunakan dalam satu baris.
sumber