Scala: ResultSet translation into a different kinds of multimaps

1.1k Views Asked by At

I am going to create wrapper over JDBC ResultSet in Scala.
This wrapper is intended to be a function ResultSet => ParticularType.
The problem is I can't find a common solution for making MultiMaps.

Here is the way I am fetching collections:

abstract class CollectionCreator[C] extends (ResultSet => C) {  
  def apply(rs: ResultSet): C = {  
    do { append(rs) } while (rs.next)  
    returnCollection()  
  }  
  def append(rs: ResultSet)  
  def returnCollection(): C 
}

Next goes maps creation. It is an implementation of collection creation and it is not abstract due to map non-abstractness (it is always backended with HashMap in my implementation).
In my opinion, it should look like this:

class MapCreator[K,IV](keyCreator: ResultSet => K, valueCreator: ResultSet => IV)
  extends CollectionCreator[Map[K, Place for V]] {

  type V = IV
  val intermediateMap = new HashMap[K, V]

  override def append(rs: ResultSet) {
    appendToMap(keyCreator(rs), valueCreator(rs))
  }

  def appendToMap(key: K, value: IV) {
    intermediateMap(key) = value
  }

  override def returnCollection(): Map[K,V] = intermediateMap.toMap
}

If it works, I would write ListMultiMap creation this way:

class ListMultiMapCreator[K,IV](keyCreator: ResultSet => K, valueCreator: ResultSet => IV)
  extends MapCreator[K, Place for V](keyCreator, valueCreator) {

  override type V = List[IV]

  override def appendToMap(key: K, value: IV) {
    intermediateMap(key) = intermediateMap.get(key) match {
      case Some(values) => values.::(value)
      case None         => List(value)
    }
  }
}

The problem is I can't use V at Place for V because it is not declared then.
I feel like abstract types is the good solution but don't know how to treat them right.

What is the correct way of such a collections creation?

I am also not sure if it is possible to override abstract types that were already defined higher in the class hierarchy.

1

There are 1 best solutions below

1
On BEST ANSWER

In your definition, MapCreator is really constraining IV and V to be the same type. By contract the V returned in Map[K,V] has to be the same type as the type returned by valueCreator. For instance, if I called:

MapCreator((rs:ResultSet) => rs.getString("KEY"),
    (rs:ResultSet) => rs.getString("VALUE"))(resultSet)

I would expect to get a Map[String,String]. You cannot change that relation if you extend MapCreator. If you want a Map[String,List[String]] you need to supply a valueCreator of type (ResultSet) => List[String].

With that in mind you can change the definition and implementation like this:

class MapCreator[K,IV](keyCreator: ResultSet => K, 
    valueCreator: ResultSet => IV) extends CollectionCreator[Map[K, IV]] { 
  val intermediateMap = new HashMap[K, IV]
  def append(rs: ResultSet) { appendToMap(keyCreator(rs), valueCreator(rs)) }
  def appendToMap(key: K, value: IV) { intermediateMap(key) = value }
  def returnCollection(): Map[K, IV] = intermediateMap.toMap
}

class ListMultiMapCreator[K,IV](keyCreator: ResultSet => K, 
    elemCreator: ResultSet => IV) extends 
  MapCreator[K, List[IV]](keyCreator, 
      (rs:ResultSet) => List(elemCreator(rs))) {
  override def appendToMap(key: K, value: List[IV]) {
    intermediateMap(key) = intermediateMap.get(key) match {
      case Some(values) => values.:::(value)
      case None         => value
    }
  }
}

I feel that because CollectionCreator uses a type parameter, that it will be cumbersome to use abstract types. Overall, there seems to be much boiler plate. I would leverage more of the scala libraries:

def mapCreate[K, IV](rs: ResultSet, 
    keyCreator: ResultSet => K, 
    valueCreator: ResultSet => IV) = {
  Iterator.continually(rs).takeWhile(_.next).map{rs => 
    keyCreator(rs) -> valueCreator(rs)}.toMap
}

def listMultiMapCreate[K, IV](rs: ResultSet, 
    keyCreator: ResultSet => K, 
    valueCreator: ResultSet => IV) = {
  Iterator.continually(rs).takeWhile(_.next).map{rs => 
    keyCreator(rs) -> valueCreator(rs)}.toList.groupBy(_._1)
}

Also in your do { append(rs) } while (rs.next) what if the result set is empty?