Pertanyaan Memperlakukan SQL ResultSet seperti Scala Stream


Ketika saya query database dan menerima kembali (ResultS only, read-only) ResultSet kembali, ResultSet bertindak seperti daftar baris database.

Saya mencoba mencari cara untuk memperlakukan ResultSet ini seperti Scala Stream. Ini akan memungkinkan operasi seperti itu filter, map, dll., sementara tidak mengkonsumsi RAM dalam jumlah besar.

Saya menerapkan metode tail-recursive untuk mengekstrak item individual, tetapi ini mengharuskan semua item berada dalam memori pada saat yang sama, masalah jika ResultSet sangat besar:

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

38
2018-03-09 15:25


asal


Jawaban:


Saya tidak mengujinya, tetapi mengapa itu tidak berhasil?

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

65
2018-03-09 17:50



Fungsi utilitas untuk jawaban @ elbowich:

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

Memungkinkan Anda untuk menggunakan inferensi jenis. Misalnya.:

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

9
2018-04-30 08:33



Ini terdengar seperti peluang besar untuk kelas implisit. Pertama definisikan kelas implisit di suatu tempat:

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

Selanjutnya, cukup impor kelas implisit ini di mana pun Anda telah mengeksekusi kueri Anda dan tentukan objek ResultSet:

import com.company.Implicits._

Akhirnya, dapatkan data dengan menggunakan metode toStream. Misalnya, dapatkan semua id seperti yang ditunjukkan di bawah ini:

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

7
2017-09-29 16:31



saya membutuhkan sesuatu yang serupa. Membangun jawaban elbowich yang sangat keren, saya membungkusnya sedikit, dan bukannya string, saya mengembalikan hasilnya (sehingga Anda bisa mendapatkan kolom apa pun)

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

Saya perlu mengakses metadata tabel, tetapi ini akan berfungsi untuk baris tabel (bisa melakukan stmt.executeQuery (sql) daripada md.getColumns):

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

3
2017-08-19 19:40



Karena ResultSet hanya objek yang dapat dipindahkan yang dinavigasi oleh berikutnya, kita perlu mendefinisikan konsep kita sendiri dari baris berikutnya. Kita dapat melakukannya dengan fungsi input sebagai berikut:

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.")
}

EDIT: Terjemahkan ke streaming atau sesuatu yang lain seperti di atas.


2
2018-05-17 04:57



Implementasi ini, meskipun lebih panjang dan clumsier itu dalam korespondensi yang lebih baik dengan kontrak ResultSet. Efek samping telah dihapus dari hasNext (...) dan pindah ke next ().

new Iterator[String] {
  private var available = resultSet.next()
  override def hasNext: Boolean = available
  override def next(): String = {
    val string = resultSet.getString(1)
    available = resultSet.next()
    string
  }
}

0
2018-05-22 22:15