*
yöntem:
Bu, varsayılan projeksiyonu döndürür - siz nasıl tarif edersiniz:
' genellikle ilgilendiğim tüm sütunlar (veya hesaplanan değerler) '.
Tablonuzda birkaç alan olabilir; varsayılan projeksiyonunuz için yalnızca bir alt kümeye ihtiyacınız vardır. Varsayılan projeksiyon, tablonun tür parametreleriyle eşleşmelidir.
Her seferinde bir tane alalım. Malzemeler olmadan <>
, sadece *
:
object Bars extends Table[(Int, String)]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id ~ name
}
Bunun gibi bir tablo tanımı, aşağıdaki gibi sorgular yapmanızı sağlar:
implicit val session: Session =
val result = Query(Bars).list
bu
gibi basit sorgular için varsayılan projeksiyon (Int, String)
a yol açar List[(Int, String)]
.
val q =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1)
Türü nedir q
? Bu bir Query
projeksiyon ile (String, Int)
. Çağrıldığında, projeksiyona List
göre bir (String, Int)
tuple döndürür .
val result: List[(String, Int)] = q.list
Bu durumda, anlama yield
cümlesinde istediğiniz projeksiyonu tanımlamış olursunuz for
.
Şimdi <>
ve hakkında Bar.unapply
.
Bu, Haritalanmış Projeksiyonlar olarak adlandırılan şeyi sağlar .
Şimdiye kadar, kayganlığın Scala'da sütunların projeksiyonunu (veya hesaplanan değerleri) döndüren sorguları ifade etmenize nasıl izin verdiğini gördük ; Dolayısıyla, bu sorguları yürütürken , bir sorgunun sonuç satırını bir Scala tuple olarak düşünmeniz gerekir . Demetin türü, tanımlanan Projeksiyon ile eşleşecektir ( for
önceki örnekte olduğu gibi, sizin
anlayışınız tarafından, varsayılan *
projeksiyon tarafından ). Bu yüzden field1 ~ field2
bir projeksiyon döndürür Projection2[A, B]
nereye
A
türüdür field1
ve B
türüdür field2
.
q.list.map {
case (name, n) =>
}
Queury(Bars).list.map {
case (id, name) =>
}
Çok fazla sütunumuz varsa kullanışsız olabilecek tuple'larla uğraşıyoruz. Sonuçları TupleN
, adlandırılmış alanlara sahip bir nesne olarak değil, bir nesne olarak düşünmek istiyoruz .
(id ~ name)
case class Bar(id: Int, name: String)
(id ~ name <> (Bar, Bar.unapply _))
Query(Bars).list.map ( b.name )
Bu nasıl çalışıyor? <>
bir projeksiyon alır Projection2[Int, String]
ve tipte eşlenmiş bir projeksiyon döndürür Bar
. İki argüman, Bar, Bar.unapply _
bu (Int, String)
projeksiyonun bir vaka sınıfına nasıl eşlenmesi gerektiğini slick söyler .
Bu iki yönlü bir haritalamadır; Bar
durum sınıfı yapıcısıdır, bu nedenle bir'den a'ya gitmek (id: Int, name: String)
için gereken bilgi budur Bar
. Ve unapply
eğer tahmin ettiyseniz, bunun tam tersi.
Nereden unapply
geliyor? Bu, herhangi bir sıradan olgu sınıf için kullanılabilir bir standart Scala yöntemidir - sadece tanımlayan Bar
bir verir Bar.unapply
bir olan çıkarıcı geri almak için kullanılabilir id
ve name
bu
Bar
ile inşa edilmiştir:
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)
Böylece, varsayılan projeksiyonunuz kullanmayı en çok beklediğiniz vaka sınıfıyla eşlenebilir:
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 _)
}
Ya da her sorguya sahip olabilirsiniz:
case class Baz(name: String, num: Int)
val q1 =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1 <> (Baz, Baz.unapply _))
İşte tipi q1
bir olan Query
bir ile eşleştirilmiş için projeksiyon Baz
. Çağrıldığında, bir döner List
ait Baz
nesneler:
val result: List[Baz] = q1.list
Son olarak, bir kenara, Option Lifting - olmayabilecek değerlerle başa çıkmanın Scala yolunu .?
sunar .
(id ~ name)
(id.? ~ name)
Hangisi, özetlemek, orijinal tanımınızla iyi bir şekilde çalışacaktır 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
Slick'in for
anlamaları nasıl kullandığı konusundaki yoruma yanıt olarak :
Her nasılsa, monadlar her zaman ortaya çıkmayı başarır ve açıklamanın bir parçası olmayı talep eder ...
Anlayışlar yalnızca koleksiyonlara özgü değildir. Her tür Monad üzerinde kullanılabilirler ve koleksiyonlar, Scala'da bulunan birçok monad türünden sadece biridir.
Ancak koleksiyonlar bilindiklerinden, açıklama için iyi bir başlangıç noktası oluştururlar:
val ns = 1 to 100 toList;
val result =
for { i <- ns if i*i % 2 == 0 }
yield (i*i)
Scala'da, anlama yöntemi, yöntem için sözdizimsel şekerdir (muhtemelen iç içe geçmiş) yöntem çağrıları: Yukarıdaki kod (aşağı yukarı) şuna eşdeğerdir:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
Temel olarak, her şey ile filter
, map
, flatMap
yöntemleri (diğer bir deyişle, bir Monad ) bir kullanılabilir
for
yerine anlama ns
. İyi bir örnek, Option monad'tır . İşte aynı for
ifadenin hem monadlar List
hem de Option
monadlar üzerinde çalıştığı
önceki örnek :
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
Son örnekte, dönüşüm belki şöyle görünecektir:
val result =
ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
result =
ns.flatMap(i => evenSqr(i))
Slick'te sorgular monadiktir - sadece map
, flatMap
ve filter
yöntemlerine sahip nesnelerdir . Dolayısıyla, for
kavrama ( *
yöntemin açıklamasında gösterilen ) sadece şu anlama gelir:
val q =
Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
val r: List[(String, Int)] = q.list
Gördüğünüz gibi flatMap
, map
ve
her bir ve çağrısıyla ' nın tekrarlanan dönüşümü ile filter
bir oluşturmak için kullanılır . Koleksiyonlar söz konusu olduğunda, bu yöntemler aslında koleksiyonu yineler ve filtreler, ancak Slick'te SQL oluşturmak için kullanılırlar. Daha fazla ayrıntı burada:
Scala Slick, Scala kodunu JDBC'ye nasıl çevirir?Query
Query(Bars)
filter
map