2012-12-16 46 views
85

मैं कुछ स्लिम कार्यों को समझने की कोशिश करता हूं और इसकी क्या आवश्यकता है।स्कैला स्लिक विधि मैं अब तक नहीं समझ सकता

यहाँ यह एक उदाहरण:

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

किसी ने मुझसे समझा सकते हैं * विधि यहाँ के प्रयोजन क्या है, क्या <> है, क्यों unapply? और प्रक्षेपण क्या है - विधि ~ 'Projection2 का उदाहरण देता है?

उत्तर

189

[अद्यतन] - जोड़ा (अभी तक एक) for comprehensions पर स्पष्टीकरण

  1. * विधि:

    यह डिफ़ॉल्ट प्रक्षेपण रिटर्न - जो है आप कैसे वर्णन:

    'सभी कॉलम (या अभिकलन मान) मैं आमतौर पर कर रहा हूँ रुचि' में

    आपका तालिका कई क्षेत्रों हो सकता था।; आपको केवल के लिए अपने डिफ़ॉल्ट प्रक्षेपण के लिए एक सबसेट की आवश्यकता है। डिफ़ॉल्ट प्रक्षेपण तालिका के पैरामीटर से मेल खाना चाहिए।

    चलिए इसे एक समय में लेते हैं।<> सामान के बिना, बस *:

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

    बस ऐसे ही एक मेज परिभाषा आप की तरह के प्रश्नों बनाने देंगे:

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

    (Int, String) के डिफ़ॉल्ट प्रक्षेपण सरल प्रश्नों इस तरह के लिए एक 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. 
    

    q का प्रकार क्या है? प्रक्षेपण (String, Int) के साथ यह Query है। जब लागू किया जाता है, तो यह प्रक्षेपण के अनुसार (String, Int) tuples के List देता है।

    val result: List[(String, Int)] = q.list 
    

    इस मामले में, आप प्रक्षेपण आप for समझ की yield खंड में चाहते परिभाषित किया है।

  2. अब लगभग <> और Bar.unapply

    यह मैप किए गए अनुमान कहलाता है।

    अब तक हमने देखा है कि स्काल में क्वेरी कैसे व्यक्त करने की अनुमति देता है जो कॉलम (या गणना मूल्य) के प्रक्षेपण को वापस लौटाता है; तो निष्पादित करते समय इन प्रश्नों आपको एक प्रश्न के परिणाम पंक्ति के परिणाम स्केल टुपल के रूप में सोचना होगा। टुपल का प्रकार परिभाषित प्रक्षेपण से मेल खाता है (आपके for पिछले उदाहरण के अनुसार डिफ़ॉल्ट * प्रक्षेपण द्वारा समझ में आता है)। यही कारण है कि field1 ~ field2Projection2[A, B] का प्रक्षेपण देता है जहां Afield1 और B का प्रकार 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 
    } 
    

    हम tuples, जो बोझिल अगर हम भी कई स्तंभ हो सकता है के साथ काम कर रहे हैं। हम परिणामों के बारे में सोचना नहीं चाहते हैं TupleN बल्कि नामित फ़ील्ड वाले कुछ ऑब्जेक्ट।

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

    यह कैसे काम करता है? <> प्रक्षेपण Projection2[Int, String] और Bar पर एक मैप किए गए प्रक्षेपण को वापस लेता है। दो तर्क Bar, Bar.unapply _ बताएं कि यह (Int, String) प्रोजेक्शन को किसी केस क्लास में मैप किया जाना चाहिए।

    यह दो-तरफा मैपिंग है; Bar केस क्लास कन्स्ट्रक्टर है, इसलिए (id: Int, name: String) से Bar पर जाने के लिए आवश्यक जानकारी है। और unapply यदि आपने अनुमान लगाया है, तो रिवर्स के लिए है।

    unapply कहां से आता है?

    : बस को परिभाषित करने Bar आप एक Bar.unapply जो एक निकालने कि id और name कि Bar साथ बनाया गया था वापस पाने के लिए इस्तेमाल किया जा सकता है देता है - यह एक मानक स्काला विधि के लिए उपलब्ध किसी भी साधारण मामला वर्ग है
    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)] 
    

    तो अपने डिफ़ॉल्ट प्रक्षेपण मामले वर्ग के लिए मैप किया जा सकता आप सबसे अधिक उपयोग करने की उम्मीद:

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

    या आप भी यह हो सकता है प्रति-प्रश्न:

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

    यहाँ q1 के प्रकार के एक के साथ एक QueryBaz को प्रक्षेपण मैप किया गया है। जब लागू, यह Baz के List वस्तुओं रिटर्न:

    val result: List[Baz] = q1.list 
    
  3. अंत में, एक अलग रूप में, .?विकल्प भारोत्तोलन प्रदान करता है के रूप में - की स्काला रास्ता मान जो नहीं हो सकता है के साथ काम कर।

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

    कौन सा, ऊपर लपेटकर, 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. टिप्पणी के जवाब में पर कैसे स्लिक का उपयोग करता है for comprehensions:

    किसी तरह, monads हमेशा को दिखाने और मांग करने का प्रबंधन स्पष्टीकरण का हिस्सा बनें ...

    समझ के लिए केवल संग्रह के लिए विशिष्ट नहीं हैं। उनका उपयोग किसी भी प्रकार के मोनाड पर किया जा सकता है, और संग्रह स्कैला में उपलब्ध कई प्रकार के मोनड प्रकारों में से एक है।

    लेकिन जैसे संग्रह परिचित हैं, वे एक अच्छा प्रारंभिक बिंदु एक स्पष्टीकरण के लिए करते हैं:

    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, ...) 
    

    स्काला में, समझ के लिए एक वाक्यात्मक चीनी विधि (संभवतः नेस्ट) विधि कॉल के लिए है: उपरोक्त कोड है (कम या ज्यादा) बराबर करने के लिए:

    ns.filter(i => i*i % 2 == 0).map(i => i*i) 
    

    असल में, filter के साथ कुछ भी, map, flatMap विधियों (दूसरे शब्दों में, मोनाड) ns की जगह for समझ में उपयोग किया जा सकता है। एक अच्छा उदाहरण Option monad है।यहाँ पिछले उदाहरण जहां एक ही for बयान दोनों List पर काम करता है और साथ ही Option monads है:

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

    पिछले उदाहरण में, परिवर्तन शायद लगेगा इस तरह:

    // 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 में, प्रश्न monadic हैं - वे केवल map, flatMap और filter विधियों के साथ ऑब्जेक्ट्स हैं। तो for समझ (* विधि के विवरण में दिखाया गया है) सिर्फ तब्दील करने के लिए:

    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 
    

    आप देख सकते हैं, flatMap, map और filter लिए उपयोग किया जाता Query(Bars) के बार-बार परिवर्तन द्वारा एक Query उत्पन्न filter और map के प्रत्येक आमंत्रण के साथ। संग्रहों के मामले में इन विधियों को वास्तव में पुन: सक्रिय करें और संग्रह फ़िल्टर करें लेकिन स्लिक में उनका उपयोग SQL उत्पन्न करने के लिए किया जाता है। यहां अधिक जानकारी: How does Scala Slick translate Scala code into JDBC?

+11

सुंदर जवाब। –

+0

'1' स्पष्टीकरण ब्लॉक में: यह स्पष्ट नहीं है कि 'val q =' WrappingQuery है, यह कोड पढ़ने के दौरान एक सूची जैसा दिखता है। यह कैसे संभव है कि यह क्वेरी में बदल जाता है ..? (मैं अभी भी यह समझने के लिए आपके स्पष्टीकरण के साथ खेल रहा हूं कि यह कैसे काम करता है। इसके लिए धन्यवाद!) – ses

+0

@ses - इस बारे में एक (थोड़ा लंबा) स्पष्टीकरण जोड़ा गया ... इसके अलावा, http://stackoverflow.com/questions पर देखें/13454347/monads-with-java-8/13455602 # 13455602 - मुझे एहसास हुआ कि यह लगभग एक ही सामग्री है। – Faiz

6

चूंकि किसी और ने उत्तर नहीं दिया है, इससे आपको प्रारंभ करने में मदद मिल सकती है। मैं बहुत अच्छी तरह से स्लिम नहीं जानता।

Slick documentation से:

लिफ़्टेड एम्बेडिंग:

हर तालिका एक * विधि एक डिफ़ॉल्ट प्रक्षेपण contatining की आवश्यकता है। यह बताता है कि जब आप पंक्तियों को वापस करते हैं ( तालिका ऑब्जेक्ट के रूप में) क्वेरी से आप वापस क्या प्राप्त करते हैं। Slick's * प्रक्षेपण के पास डेटाबेस में से किसी एक से मेल नहीं खाता है। आप नए कॉलम जोड़ सकते हैं (उदा। गणना मानों के साथ) या कुछ कॉलम को अपनी पसंद के अनुसार छोड़ दें। * प्रक्षेपण के अनुरूप गैर-उठाए गए प्रकार को तालिका में एक प्रकार पैरामीटर के रूप में दिया जाता है। सरल, गैर-मैप किए गए तालिकाओं के लिए, यह एक कॉलम प्रकार या कॉलम प्रकारों का टुपल होगा।

दूसरे शब्दों में, स्लिक को डेटाबेस से लौटाई गई पंक्ति से निपटने के तरीके की आवश्यकता है। आपके द्वारा परिभाषित विधि में आपके कॉलर परिभाषाओं को उस पंक्ति में उपयोग करने के लिए अपने पार्सर संयोजक फ़ंक्शंस का उपयोग किया जाता है जिसका उपयोग पंक्ति पर किया जा सकता है।

+0

ओक। और प्रोजेक्शन सिर्फ कॉलम की एक प्रतिनिधित्व है .. जैसे: अंतिम वर्ग Projection2 [T1, T2] ( ओवरराइड वैल _1: कॉलम [टी 1], ओवरराइड वैल _2: कॉलम [टी 2] ) फैली Tuple2 (_1, _2) प्रक्षेपण के साथ [(टी 1, टी 2)] {.. – ses

+0

अब .. यह कैसे आ रहा है: बार में 'अनुपयोगी' विधि है? – ses

+2

आह .. - सभी केस वर्ग उत्पाद विशेषता को लागू करते हैं, और अनुपयोगी उत्पाद की विधि है। जादू। – ses