2012-03-09 21 views
33

जैसे SQL परिणामसेट का इलाज करना जब मैं डेटाबेस से पूछता हूं और एक (केवल-पढ़ने के लिए, केवल-पढ़ने के लिए) परिणाम प्राप्त करता हूं, तो ResultSet डेटाबेस पंक्तियों की सूची की तरह कार्य करता है।स्केल स्ट्रीम

मैं इस परिणामसेट को स्केल Stream की तरह इलाज करने का कोई तरीका ढूंढने की कोशिश कर रहा हूं। यह रैम की बड़ी मात्रा में उपभोग नहीं करते समय filter, map, आदि जैसे परिचालनों की अनुमति देगा।

मैं अलग-अलग आइटम को निकालने के लिए एक पूंछ पुनरावर्ती पद्धति लागू है, लेकिन यह है कि सभी आइटम एक समस्या एक ही समय में स्मृति में हो, अगर ResultSet बहुत बड़ी है की आवश्यकता है:

// Iterate through the result set and gather all of the String values into a list 
// then return that list 
@tailrec 
def loop(resultSet: ResultSet, 
     accumulator: List[String] = List()): List[String] = { 
    if (!resultSet.next) accumulator.reverse 
    else { 
    val value = resultSet.getString(1) 
    loop(resultSet, value +: accumulator) 
    } 
} 
+0

आप आप क्या चाहते करने के लिए एक स्ट्रीम के बजाय एक Iterable का उपयोग किया जा सका: उदाहरण के लिए, सभी आईडी के रूप में नीचे दिखाया गया है? –

+3

इसके अलावा एक स्ट्रीम मेमोरी में मानों को बनाए रखेगी, इसलिए जब आप सूची के अंत तक पहुंच जाएंगे तब तक आप वास्तव में स्मृति को सहेज नहीं पाएंगे। –

+0

मुझे लगता है कि जेडीबीसी ध्वज/विकल्प के बिना जेडीबीसी स्वयं परिणामों को स्ट्रीम करता है, आपके पास अभी भी आपके जेडीबीसी एपीआई द्वारा निर्मित मेमोरी में डेटा की एक पूर्ण प्रति है। – matanster

उत्तर

61

मैं नहीं था ' टी परीक्षण, लेकिन यह क्यों काम नहीं करेगा?

new Iterator[String] { 
    def hasNext = resultSet.next() 
    def next() = resultSet.getString(1) 
}.toStream 
+0

यह सही दिखता है। जैसे ही मैं अपना डेटाबेस सेट अप करता हूं, मैं इसका परीक्षण करूंगा। मुझे यह भी नहीं लगता कि मुझे इसे 'स्ट्रीम' में बदलने की जरूरत है। मैं सीधे 'मानचित्र', 'फ़िल्टर', आदि को लागू कर सकता हूं। – Ralph

+1

कोशिश की और यह एक आकर्षण की तरह काम किया! धन्यवाद। – Ralph

+1

मैं आपको दूसरा अप-वोट देना चाहता हूं।मैंने अपने स्कैला स्निपेट लाइब्रेरी में यह कोड खंड जोड़ा है। यह जल्दी से मेरे पसंदीदा में से एक बन रहा है। – Ralph

3

मुझे कुछ इसी तरह की आवश्यकता थी। elbowich के बहुत शांत जवाब पर बिल्डिंग, मैं यह थोड़ा लिपटे, और स्ट्रिंग के बजाय, मैं परिणाम (किसी भी स्तंभ ताकि आप प्राप्त कर सकते हैं) लौट

def resultSetItr(resultSet: ResultSet): Stream[ResultSet] = { 
    new Iterator[ResultSet] { 
     def hasNext = resultSet.next() 
     def next() = resultSet 
    }.toStream 
    } 

मैं मेज मेटाडाटा का उपयोग करने की जरूरत है, लेकिन इस के लिए काम करेंगे तालिका पंक्तियों (एक stmt.executeQuery (एसक्यूएल कर सकता है) md.getColumns के बजाय): @ elbowich के जवाब के लिए

val md = connection.getMetaData() 
val columnItr = resultSetItr(md.getColumns(null, null, "MyTable", null)) 
     val columns = columnItr.map(col => { 
     val columnType = col.getString("TYPE_NAME") 
     val columnName = col.getString("COLUMN_NAME") 
     val columnSize = col.getString("COLUMN_SIZE") 
     new Column(columnName, columnType, columnSize.toInt, false) 
     }) 
+1

यदि आपको स्ट्रीम पर वापस जाने की आवश्यकता नहीं है (उदा।, केवल पुनरावृत्ति को आगे बढ़ाएं), तो आप केवल एक इटरेटर का उपयोग कर सकते हैं। यह एक स्ट्रीम का उपयोग करने के मेमोरी ओवरहेड को बहुत कम करता है (एक 'इटरेटर [परिणामसेट]' वापस लौटाता है, और 'toStream' ड्रॉप) – Greg

8

उपयोगिता समारोह:

def results[T](resultSet: ResultSet)(f: ResultSet => T) = { 
    new Iterator[T] { 
    def hasNext = resultSet.next() 
    def next() = f(resultSet) 
    } 
} 

आपको टाइप अनुमान का उपयोग करने की अनुमति देता है। उदा .:

stmt.execute("SELECT mystr, myint FROM mytable") 

// Example 1: 
val it = results(stmt.resultSet) { 
    case rs => rs.getString(1) -> 100 * rs.getInt(2) 
} 
val m = it.toMap // Map[String, Int] 

// Example 2: 
val it = results(stmt.resultSet)(_.getString(1)) 
2

क्योंकि ResultSet सिर्फ एक परिवर्तनशील वस्तु अगले द्वारा संचालित किया जा रहा है, हम एक अगली पंक्ति की अपनी अवधारणा को परिभाषित करने की जरूरत है। इस प्रकार हम एक इनपुट समारोह के साथ ऐसा कर सकते हैं:

class ResultSetIterator[T](rs: ResultSet, nextRowFunc: ResultSet => T) 
extends Iterator[T] { 

    private var nextVal: Option[T] = None 

    override def hasNext: Boolean = { 
    val ret = rs.next() 
    if(ret) { 
     nextVal = Some(nextRowFunc(rs)) 
    } else { 
     nextVal = None 
    } 
    ret 
    } 

    override def next(): T = nextVal.getOrElse { 
    hasNext 
    nextVal.getOrElse(throw new ResultSetIteratorOutOfBoundsException 
)} 

    class ResultSetIteratorOutOfBoundsException extends Exception("ResultSetIterator reached end of list and next can no longer be called. hasNext should return false.") 
} 

संपादित करें: स्ट्रीम या इसके बाद के संस्करण के अनुसार कुछ और करने के लिए अनुवाद।

5

यह एक निहित वर्ग के लिए एक शानदार अवसर की तरह लगता है। सबसे पहले कहीं निहित वर्ग को परिभाषित:

import java.sql.ResultSet 

object Implicits { 

    implicit class ResultSetStream(resultSet: ResultSet) { 

     def toStream: Stream[ResultSet] = { 
      new Iterator[ResultSet] { 
       def hasNext = resultSet.next() 

       def next() = resultSet 
      }.toStream 
     } 
    } 
} 

इसके बाद, बस इस अंतर्निहित वर्ग आयात भी आप आपकी क्वेरी निष्पादित और ResultSet वस्तु को परिभाषित किया है:

import com.company.Implicits._ 

अंत में toStream पद्धति का उपयोग करके बाहर डेटा मिलता है।

val allIds = resultSet.toStream.map(result => result.getInt("id"))