scala slick method şu ana kadar anlayamıyorum


89

Bazı Slick çalışmalarını ve ne gerektirdiğini anlamaya çalışıyorum.

İşte bir örnek:

package models

case class Bar(id: Option[Int] = None, name: String)

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  // This is the primary key column
  def name = column[String]("name")

  // Every table needs a * projection with the same type as the table's type parameter
  def * = id.? ~ name <>(Bar, Bar.unapply _)
}

Biri bana *burada yöntemin amacının ne olduğunu <>, neden olduğunu açıklayabilir unapplymi? ve Projeksiyon nedir - yöntem ~'örneğini döndürür Projection2?

Yanıtlar:


198

[GÜNCELLEME] - kavramalarla ilgili (yine bir başka) açıklama eklendifor

  1. *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 *:

    // 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)
    

    Bunun gibi bir tablo tanımı, aşağıdaki gibi sorgular yapmanızı sağlar:

    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)]
    

    bu gibi basit sorgular için varsayılan projeksiyon (Int, String)a yol açar List[(Int, String)].

    // 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.
    

    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.

  2. Ş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) =>  // do something with name:String and n:Int
    }
    
    Queury(Bars).list.map {
      case (id, name) =>  // do something with id:Int and name:String 
    }
    

    Ç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)  // 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.
    

    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")
    // 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)]
    

    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)
    
    // 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 _))
    

    İş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
    
  3. Son olarak, bir kenara, Option Lifting - olmayabilecek değerlerle başa çıkmanın Scala yolunu .?sunar .

     (id ~ name)   // Projection2[Int, String] // this is just for illustration
     (id.? ~ name) // Projection2[Option[Int], String]
    

    Hangisi, özetlemek, orijinal tanımınızla iyi bir şekilde çalışacaktır 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]
    
  4. 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; // 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, ...)
    

    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 :

    // (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
    

    Son örnekte, dönüşüm belki şöyle görünecektir:

    // 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)) 
    

    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)
    // Type of q is Query[(String, Int)]
    
    val r: List[(String, Int)] = q.list // Actually run the query
    

    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


'1' açıklama bloğunda: 'val q =' nun WrappingQuery olduğu açık değildir, kodu okurken List <Projection2> gibi görünür. Query'ye dönüşmesi nasıl mümkün olabilir ..? (Nasıl çalıştığını anlamak için hala açıklamalarınızla oynuyorum. Bunun için teşekkürler!)
ses

@ses - bununla ilgili (biraz uzun) bir açıklama ekledi ... Ayrıca, stackoverflow.com/questions/13454347/monads-with-java-8/… 'a bakın - Neredeyse aynı içerik olduğunu anladım.
Faiz

Gizemli derleme hataları yaşayanlar için foo kullanın. Seçenek [T] sütunları için yoksa, okunması zor bir tür uyuşmazlığı yaşarsınız. Teşekkürler Faiz!
sventechie

1
Bu harika bir cevap ... Slick 3.0 için güncellenebilseydi harika olurdu
Ixx

6

Kimse cevap vermediğine göre bu, başlamanıza yardımcı olabilir. Slick'i pek iyi tanımıyorum.

Gönderen Kaygan belgelerinde :

Kaldırılmış Gömme:

Her tablo, varsayılan bir projeksiyonu içeren bir * yöntemi gerektirir. Bu, bir sorgudan satırları (tablo nesnesi biçiminde) döndürdüğünüzde geri aldığınız şeyi açıklar. Slick'in * projeksiyonunun veritabanındaki ile eşleşmesi gerekmez. Yeni sütunlar ekleyebilir (örneğin hesaplanan değerlerle) veya bazı sütunları istediğiniz gibi çıkarabilirsiniz. * Projeksiyona karşılık gelen kaldırılmamış tip, Tabloya tip parametresi olarak verilmiştir. Basit, eşlenmemiş tablolar için bu, tek bir sütun türü veya bir sütun türü demeti olacaktır.

Başka bir deyişle, slick'in veritabanından döndürülen bir satırla nasıl başa çıkılacağını bilmesi gerekir. Tanımladığınız yöntem, sütun tanımlarınızı bir satırda kullanılabilecek bir şeyde birleştirmek için ayrıştırıcı birleştirici işlevlerini kullanır.


ook. ve Projeksiyon, yalnızca sütunların bir temsilidir .. örneğin: son sınıf Projeksiyon2 [T1, T2] (val _1: Sütun [T1], geçersiz kılma val _2: Sütun [T2]) Tuple2 (_1, _2) ile Projeksiyon [( T1, T2)] {..
ses

Şimdi .. nasıl oluyor da: Bar'ın 'uygulanmayan' yöntemi var?
ses

2
Aha .. - tüm vaka sınıfları Product özelliğini uygular ve unapply, Product'ın yöntemidir. Büyü.
ses
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.