Saturday, July 30, 2011

Eager Fetching and Mapping Entities with Scala

Note: This post assumes you are doing the sql-results to objects mapping manually. Which I find it is the best approach (we'll leave the reasons for another post).

Lets say we have an Employee entity that is related (in the database) to a few other entities.
case class Employee (firstName: String, lastName :String)
case class Department (name:String)
case class Project (name:String)

To model the relationship, we COULD create a field on the Employee entity that references the department and the project:
case class Employee (
  firstName :String, 
  lastName :String, 
  department :Department,  // <---
  project :Project         // <---
)
But when displaying a page it might turn out that we only need the employee info, not the department or project. When retrieving them from the database, we might choose to leave those null, but that will mean we'll get an NPE (at runtime of course: very bad) if we happen to reference one of the related entities on the page. We might also choose to use Option but that's not entirely accurate, since they are not "Optional" fields, we KNOW that for a particular page we WILL need, say, the Department to be there.

This is where traits and the ability to apply them at instantiation comes in handy.

As an example, let say I would like to display a list of employees with their respective departments. I can create traits that specify that an entity comes with a related entity:
trait HasDepartment {
  val department :Department
}
trait HasProject {
  val project :Project
}
Now, when rendering the table of employees with their departments, I can use the trait to specify the required entity:
def listEmployees (projects:List[Employee with HasDepartment]) = {
  <table>
    <tr>
      <th>Name</th>
      <th>Department</th>
    </tr>
    {for (e <- employees) yield
      <tr>
        <td>{e.firstName} {e.lastName}</td>
        <td>{e.department.name}</td>
      </tr>
    }
  </table>
}
The requirement only specifies that the Employee must have its related Department. If we try to display the related Project using {e.project.name} we will get a compile-time error complaining about the missing "project" property right away (very good).

Now, how do we actually add the related Department to the Employee when querying the database? Like this:
// let's assume we are using some basic db wrapper, like anorm
val employees = queryResults.map(row => {
  new Employee ( row[String]("employee.first_name"), row[String]("employee.last_name") ) 
      with HasDepartment {
    val department = Department( row[String]("department.name") )
  }
})
It is now encoded on the type that this Employee entity has its related Department entity (eagerly fetched). You can now decide on a case by case basis when to include related entities, and have it checked by the compiler that you are only able to use them when they have been included.

As an extra example, here is the same code that actually requires that the Project entity also be there:
def listEmployees (projects:List[Employee with HasDepartment with HasProject]) = {
  <table>
    <tr>
      <th>Name</th>
      <th>Department</th>
      <th>Project</th>
    </tr>
    {for (e <- employees) yield
      <tr>
        <td>{e.firstName} {e.lastName}</td>
        <td>{e.department.name}</td>
        <td>{e.project.name}</td>
      </tr>
    }
  </table>
}
And the code to map the employee from the database:
// let's assume we are using some basic db wrapper, like anorm
val employees = queryResults.map(row => {
  new Employee ( row[String]("employee.first_name"), row[String]("employee.last_name") ) 
      with HasDepartment with HasProject {
    val department = Department( row[String]("department.name") )
    val project = Project( row[String]("project.name") )
  }
})