HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Realizing a SQL ResultSet into a Map in Scala

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
maprealizingintosqlscalaresultset

Problem

I am trying to realize a java.sql.ResultSet into a map, in Scala.

import java.sql.{ResultSet, ResultSetMetaData}

class DbRow extends java.util.HashMap[java.lang.String, Object] {
}

object freeFunctions {

  def realize(queryResult: ResultSet): Vector[DbRow] = {
    val md = queryResult.getMetaData
    val colNames = for (i <- 1 to md.getColumnCount) yield md.getColumnName(i)
    var rows: Vector[DbRow] = Vector.empty
    while (queryResult.next()) {
      val row = new DbRow
      for (n <- colNames) {
        row.put(n, queryResult.getObject(n))
      }
      rows = rows :+ row
    }
    rows
  }

}


I feel this could (should) be less verbose. If while/yield comprehensions existed, or some
comprehension for create a map. The client code needs a java map for now, but if an elegant
enough solution can produce a Scala map, I could convert it.

Solution

I don't have the time to test this, so I hope you can do that for me. I decided not to use HashMap straight away because type conversion makes things a bit more complicated than they should, but let me know if this is important for you and causes you trouble.

Here's option 1:

def realize(queryResult: ResultSet): Vector[Map[String, Object]] = {
    val md = queryResult.getMetaData
    val colNames = for (i  (for (n  queryResult.getObject(n)).toMap
    Iterator.continually(queryResult.next()).takeWhile(identity).map(_ => buildMap()).toVector
}


I don't like it much because some parts of the code rely on side effects of other parts of the code, so even if it works, it's still ugly.

Here's option 2, which I like more:

def buildMap(queryResult: ResultSet, colNames: Seq[String]): Option[Map[String, Object]] =
    if (queryResult.next())
        Some(colNames.map(n => n -> queryResult.getObject(n)).toMap)
    else
        None

def realize(queryResult: ResultSet): Vector[Map[String, Object]] = {
    val md = queryResult.getMetaData
    val colNames = (1 to md.getColumnCount) map md.getColumnName
    Iterator.continually(buildMap(queryResult, colNames)).takeWhile(!_.isEmpty).map(_.get).toVector
}


Nicer, IMHO, you can save some dots over there but I decided to keep them. Maybe something like this:

(Iterator continually (buildMap(queryResult, colNames)) takeWhile (!_.isEmpty) map (_.get)).toVector


Again, I didn't test it, I don't know how to do it without a DB and I don't feel like setting one up now, sorry for that.

Finally, I'm not sure if you realized this, but "realize" is probably not the best name for this method (or for any method, unless you are programming some epistemic system, IMHO).

Code Snippets

def realize(queryResult: ResultSet): Vector[Map[String, Object]] = {
    val md = queryResult.getMetaData
    val colNames = for (i <- 1 to md.getColumnCount) yield md.getColumnName(i)
    val buildMap = () => (for (n <- colNames) yield n -> queryResult.getObject(n)).toMap
    Iterator.continually(queryResult.next()).takeWhile(identity).map(_ => buildMap()).toVector
}
def buildMap(queryResult: ResultSet, colNames: Seq[String]): Option[Map[String, Object]] =
    if (queryResult.next())
        Some(colNames.map(n => n -> queryResult.getObject(n)).toMap)
    else
        None

def realize(queryResult: ResultSet): Vector[Map[String, Object]] = {
    val md = queryResult.getMetaData
    val colNames = (1 to md.getColumnCount) map md.getColumnName
    Iterator.continually(buildMap(queryResult, colNames)).takeWhile(!_.isEmpty).map(_.get).toVector
}
(Iterator continually (buildMap(queryResult, colNames)) takeWhile (!_.isEmpty) map (_.get)).toVector

Context

StackExchange Code Review Q#64662, answer score: 5

Revisions (0)

No revisions yet.