Enriching JDBC statements with the "pimp my library" pattern

In the previous section, we saw how to create self-closing connections with the loan pattern. This allows us to open connections to the database without having to remember to close them. However, we still have to remember to close any ResultSet and PreparedStatement that we open:

// WARNING: Poor Scala code
SqlUtils.usingConnection("test") { connection =>
  val statement = connection.prepareStatement(
    "SELECT * FROM physicists")
  val results = statement.executeQuery
  // do something useful with the results
  results.close
  statement.close
}

Having to open and close the statement is somewhat ugly and error prone. This is another natural use case for the loan pattern. Ideally, we would like to write the following:

usingConnection("test") { connection =>
  connection.withQuery("SELECT * FROM physicists") {
    resultSet => // process results
  }
}

How can we define a .withQuery method on the Connection class? We do not control the Connection class definition as it is part of the JDBC API. We would like to be able to somehow reopen the Connection class definition to add the withQuery method.

Scala does not let us reopen classes to add new methods (a practice known as monkey-patching). We can still, however, enrich existing libraries with implicit conversions using the pimp my library pattern (http://www.artima.com/weblogs/viewpost.jsp?thread=179766). We first define a RichConnection class that contains the withQuery method. This RichConnection class is created from an existing Connection instance.

// RichConnection.scala

import java.sql.{Connection, ResultSet}

class RichConnection(val underlying:Connection) {

  /** Execute a SQL query and process the ResultSet */
  def withQuery[T](query:String)(f:ResultSet => T):T = {
    val statement = underlying.prepareStatement(query)
    val results = statement.executeQuery
    try {
      f(results) // loan the ResultSet to the client
    }
    finally {
      // Ensure all the resources get freed.
      results.close
      statement.close
    }
  }
}

We could use this class by just wrapping every Connection instance in a RichConnection instance:

// Warning: poor Scala code
SqlUtils.usingConnection("test") { connection =>
  val richConnection = new RichConnection(connection)
  richConnection.withQuery("SELECT * FROM physicists") {
    resultSet => // process resultSet
  }
}

This adds unnecessary boilerplate: we have to remember to convert every connection instance to RichConnection to use withQuery. Fortunately, Scala provides an easier way with implicit conversions: we tell Scala how to convert from Connection to RichConnection and vice versa, and tell it to perform this conversion automatically (implicitly), if necessary:

// Implicits.scala
import java.sql.Connection

// Implicit conversion methods are often put in 
// an object called Implicits.
object Implicits {
  implicit def pimpConnection(conn:Connection) = 
    new RichConnection(conn)
  implicit def depimpConnection(conn:RichConnection) =  
    conn.underlying
}

Now, whenever pimpConnection and depimpConnection are in the current scope, Scala will automatically use them to convert from Connection instances to RichConnection and back as needed.

We can now write the following (I have added type information for emphasis):

// Bring the conversion functions into the current scope
import Implicits._ 

SqlUtils.usingConnection("test") { (connection:Connection) =>
  connection.withQuery("SELECT * FROM physicists") {
    // Wow! It's like we have just added 
    // .withQuery to the JDBC Connection class!
    resultSet => // process results
  }
}

This might look like magic, so let's step back and look at what happens when we call withQuery on a Connection instance. The Scala compiler will first look to see if the class definition of Connection defines a withQuery method. When it finds that it does not, it will look for implicit methods that convert a Connection instance to a class that defines withQuery. It will find that the pimpConnection method allows conversion from Connection to RichConnection, which defines withQuery. The Scala compiler automatically uses pimpConnection to transform the Connection instance to RichConnection.

Note that we used the names pimpConnection and depimpConnection for the conversion functions, but they could have been anything. We never call these methods explicitly.

Let's summarize how to use the pimp my library pattern to add methods to an existing class:

  1. Write a class that wraps the class you want to enrich: class RichConnection(val underlying:Connection). Add all the methods that you wish the original class had.
  2. Write a method to convert from your original class to your enriched class as part of an object called (conventionally) Implicits. Make sure that you tell Scala to use this conversion automatically with the implicit keyword: implicit def pimpConnection(conn:Connection):RichConnection. You can also tell Scala to automatically convert back from the enriched class to the original class by adding the reverse conversion method.
  3. Allow implicit conversions by importing the implicit conversion methods: import Implicits._.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.162.37