Zach's Blog

Just another WordPress.com weblog

Simple JDBC Queries in Scala

with 5 comments

The Beginning Scala book has a great example of using partially applied functions to automatically close JDBC connections. Today I needed to use some complex SQL outside of our ORM and extended this code sample to make it incredibly simple & safe.

The using and bmap methods are from the book; the query and queryEach methods are my creations:

object Control {
  def using[Closeable <: {def close(): Unit}, B](closeable: Closeable)(getB: Closeable => B): B = 
    try {
      getB(closeable)
    } finally {
      closeable.close()
    }
  
  import scala.collection.mutable.ListBuffer
  
  def bmap[T](test: => Boolean)(block: => T): List[T] = {
    val ret = new ListBuffer[T]
    while(test) ret += block
    ret.toList
  }
  
  import java.sql._
  
  /** Executes the SQL and processes the result set using the specified function. */
  def query[B](connection: Connection, sql: String)(process: ResultSet => B): B =
    using (connection) { connection => 
      using (connection.createStatement) { statement => 
        using (statement.executeQuery(sql)) { results => 
          process(results)
        }
      }
    }

  /** Executes the SQL and uses the process function to convert each row into a T. */
  def queryEach[T](connection: Connection, sql: String)(process: ResultSet => T): List[T] =
    query(connection, sql) { results => 
      bmap(results.next) { 
        process(results)
      }
    }
}

The using method just ensures that something with a close() method gets closed after it’s used, while bmap collects the results of some function into a list. I found the using method resulted in a fair amount of boilerplate code, so I factored that out into the query method. You just give it a Connection object and an SQL string and it will handle creating the Statement and ResultSet, passing the ResultSet to your custom processing function, and then closing everything safely for you.

The queryEach method also eliminates some common boilerplate. It builds on the query method by using each row in the ResultSet to create some domain object and collecting all of those objects into a list. As the example usage below shows, you just give queryEach your connection and SQL, as well as some simple code to process a single row in the ResultSet:

import com.whatever.Control._
import java.sql._

val conn: Connection = ...
val people = queryEach(conn, "SELECT * FROM person") {rs => 
  new Person(rs.getString("name"), rs.getInt("age"), rs.getBoolean("valid"))
}

I think this results in some very compact and intuitive code. And reusing all of the boilerplate in the Control methods ensures that you don’t forget to close a Statement or ResultSet.

Written by Zach Cox

August 17, 2009 at 3:21 pm

Posted in Scala

Tagged with ,

5 Responses

Subscribe to comments with RSS.

  1. Great stuff. I’ve been trying to do something similar with wrapping JDBC. I expect smarter people than I can make a better go of it.

    Please take a look and tell me what you think. http://github.com/synesso/scweery

    Jeremy

    September 23, 2009 at 2:52 am

  2. @Jeremy Scweery looks pretty sweet! I like that it makes JDBC easier but isn’t a full-blown ORM. Is it in any Maven repos?

    Zach Cox

    September 23, 2009 at 7:14 am

    • Thanks Zach. It’s not in any repos as yet. I don’t think it’s quite ready for it.

      I’m still playing around with different syntax for update/insert operations. Plus there are dozens of SQL types it doesn’t support yet.

      Having said that, I have put it to good use at my work and at home for classic “data munging” operations.

      Jem

      September 23, 2009 at 7:37 am

  3. Neat; you might want to checkout querulous at https://github.com/nkallen/querulous Its released by guys at Twitter and is a super compact way to talk to the db with support for transactions.

    Ayush

    November 25, 2010 at 12:15 am

    • yea but it only works with mysql

      anon

      May 4, 2011 at 8:44 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.