Thursday, December 16, 2010

Populate Immutable Objects with iBatis Mapping

In a previous post, I provided iBatis configuration and mapping that supported populating Java objects on a per-property basis, i.e. the classes in use had to provide setters for this approach. Here I describe another iBatis pattern that populates immutable objects with more than a few properties.

First, I assume the target object uses the Builder Pattern to support construction, since this is a useful approach for immutable classes exposing, as mentioned, "more than a few properties".

Continuing from my original example, the iBatis mapping might look something like this (I omit the one-to-many issue from my original post since that's not crucial to this discussion):

<mapper namespace="com.mybiz.Student">

<resultMap id="student-info" type="map">
<result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
<result property="email" column="email" javaType="String" jdbcType="VARCHAR"/>
<result property="gmail" column="email" javaType="String" jdbcType="VARCHAR"/>
<result property="cellPhone" column="completed" javaType="String" jdbcType="VARCHAR"/>
<result property="landlinePhone" column="completed" javaType="String" jdbcType="VARCHAR"/>
<result property="googleVoicePhone" column="completed" javaType="String" jdbcType="VARCHAR"/>
<result property="gender" column="email" javaType="String" jdbcType="VARCHAR"/>
<result property="ssn" column="completed" javaType="String" jdbcType="VARCHAR"/>
<result property="ethnicity" column="completed" javaType="String" jdbcType="VARCHAR"/>

<sql id="fields">
name, email, gmail, cellPhone, landlinePhone, googleVoicePhone, gender, ssn, ethnicity

<select id="getStudentInfo" parameterClass="string" resultMap="student-info">
<include refid="fields"/>
from STUDENT where email = #value#


This mapping, in a file named e.g. student.xml, is referenced as usual in the top-level iBatis config file:

<mapper resource="com/mybiz/student.xml"/>

I'll omit the boilerplate around consuming the iBatis configuration and obtaining an iBatis session factory. Let's just go with the "miracle occurs here" approach, and cut to the Java-side chase - this first provides an interface with full classname matching the iBatis mapper namespace and a method with name matching the iBatis select statement id:

package com.mybiz;
public interface Student
String getStudentInfo(String email);

The iBatis session factory is used to open the session and obtain a concrete instance of the mapper:

SqlSession session = sessionFactory.openSession();
Student mapper = session.getMapper(Student.class);

The mapper returns a map that can be used to populate the target (immutable, remember?) object:

List<Map<String, ?>> resultMap = mapper.getStudentInfo("");

Finally, we extract values from the map and use these in an invocation of the builder for the Student object, addressing only those properties we care about:

for (Map<String, ?> result : resultMap) {
String name = (String) result.get("name");
String email = (String) result.get("email");
String gmail = (String) result.get("gmail");
String ethnicity = (String) result.get("ethnicity");
String cellPhone = (String) result.get("cellPhone");
String gender = (String) result.get("gender");
Student student = new Student.Builder(new Email(email)) // email is the only required value

Again, please refer to my post around the builder pattern to get details on how I designed the Student class to work in the above example.

No comments:

Post a Comment