views:

189

answers:

1

I've got a set of rows in a database, and I'd like to provide an interface to spin through them like this:

def findAll: Iterable[MyObject]

Where we don't require having all the instances in memory at once. In C# you can easily create generators like this using yield, the compiler takes care of converting code that loops through the recordset into an iterator (sort of inverting it).

My current code looks like this:

def findAll: List[MyObject] = {
  val rs = getRs
  val values = new ListBuffer[MyObject]
  while ( rs.next() ) 
    values += new valueFromResultSet(rs)
  values.toList
}

Is there a way I could convert this to not store the entire set in memory? Perhaps I could use a for comprehension?

+4  A: 

Try extending Iterator instead. I haven't tested it, but something like this:

def findAll: Iterator[MyObject] = new Iterator[MyObject] {
  val rs = getRs
  override def hasNext = rs.hasNext
  override def next = new valueFromResultSet(rs.next)
}

This should store rs when it's called, and otherwise just be a light wrapper to calls to rs.

If you want to save the values that you traverse, check out Stream.

Rex Kerr
I'll give that a shot, thx Rex
Alex Black
I just assumed that rs actually has a "hasNext" method. If not, you should cache the next result with a (probably private) var inside the iterator and have hasNext say whether that cached result exists.
Rex Kerr
Yeah, that works, I used hasNext = rs.isLast. One trouble is though I don't have any mechanism to close the rs and connection. In my current code I have wrapped the (above) code in a "using" method which closes things for me.
Alex Black
`hasNext = rs.isLast` doesn't seem correct.
Daniel
If "isLast" means that you can't get another one, I'd think hasNext = !rs.isLastwould be the thing to do. Maybe just a typo?
Rex Kerr
@Rex oops, yeah I meant hasNext = !rs.isLast
Alex Black