*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 Queryprojeksiyon ile (String, Int). Çağrıldığında, projeksiyona Listgöre bir (String, Int)tuple döndürür .
val result: List[(String, Int)] = q.list
Bu durumda, anlama yieldcü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 ~ field2bir projeksiyon döndürür Projection2[A, B]nereye
Atürüdür field1ve Btü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; Bardurum 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 unapplygeliyor? Bu, herhangi bir sıradan olgu sınıf için kullanılabilir bir standart Scala yöntemidir - sadece tanımlayan Barbir verir Bar.unapplybir olan çıkarıcı geri almak için kullanılabilir idve namebu
Barile 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 Querybir ile eşleştirilmiş için projeksiyon Baz. Çağrıldığında, bir döner Listait Baznesneler:
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 foranlamaları 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
foryerine anlama ns. İyi bir örnek, Option monad'tır . İşte aynı forifadenin hem monadlar Listhem de Optionmonadlar ü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, flatMapve filteryöntemlerine sahip nesnelerdir . Dolayısıyla, forkavrama ( *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, mapve
her bir ve çağrısıyla ' nın tekrarlanan dönüşümü ile filterbir 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?QueryQuery(Bars)filtermap