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"/>
</resultMap>

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

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

</mapper>

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


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

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("joe@joe.com");

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
.name(name)
.gmail(gmail)
.ethnicity(ethnicity)
.cellPhone(cellPhone)
.gender(gender)
.build();
}

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