In this part of our IBatis.NET series, we’ll go over how to compose maps to reduce redundancy. We build on the code from the prior parts of this series, so start there if you want to be fully up to speed. As in prior parts, we use the AdventureWorks database.

Map Composition

Most, if not all, modern day programmers are familiar with the DRY principle – that is, don’t repeat yourself. This is particularly important when dealing with the tedium of XML configuration that IBatis.NET is built upon. An easy example of where this might come into play would be addresses in a standard purchase order based database. You have several entities in this scenario that would have an address: a customer, a vendor, even a store, all depending on the actual scope of the current database. Take the example below from the AdventureWorks database.

addressSample

We see right away that we have three separate entities that have addresses: Employees, Vendors, and Customers. Let’s concentrate on vendors and customers, as those entities have addresses with types, and let’s assume that we want to build queries to bring back a vendor and a customer with their main office address information. A first stab at this would have our map looking like below.

<select id="GetCustomerAndPrimaryAddresses"
        resultMap="GetCustomerAndPrimaryAddressesRM">
    select
    c.AccountNumber,
    a.AddressLine1,
    a.AddressLine2,
    a.City,
    a.StateProvinceID,
    a.PostalCode
    from
    Sales.Customer as c
    inner join Sales.CustomerAddress as ca on c.CustomerID = ca.CustomerID
    inner join Person.Address as a on ca.AddressID = a.AddressID
    where
    ca.AddressTypeId = 3
</select>

<select id="GetVendorAndPrimaryAddresses"
        resultMap="GetVendorAndPrimaryAddressesRM">
    select
    v.Name,
    a.AddressLine1,
    a.AddressLine2,
    a.City,
    a.StateProvinceID,
    a.PostalCode
    from
    Purchasing.Vendor as v
    inner join Purchasing.VendorAddress as va on v.VendorID = va.VendorID
    inner join Person.Address as a on va.AddressID = a.AddressID
    where
    va.AddressTypeId = 3
</select>

and

<resultMap id="GetCustomerAndPrimaryAddressesRM"
           class="Customer">
    <result property="AccountNumber" column="AccountNumber"/>
    <result property="Line1" column="AddressLine1"/>
    <result property="Line2" column="AddressLine2"/>
    <result property="City" column="City"/>
    <result property="Zip" column="PostalCode"/>
</resultMap>

<resultMap id="GetVendorAndPrimaryAddressesRM"
           class="Vendor">
    <result property="Name" column="Name"/>
    <result property="Line1" column="AddressLine1"/>
    <result property="Line2" column="AddressLine2"/>
    <result property="City" column="City"/>
    <result property="Zip" column="PostalCode"/>
</resultMap>

You can see the problem right away. The redundancy in the queries is unavoidable; the redundancy in the maps smacks of waste. Looking at this problem from a code perspective gives us our first hint at how IBatis.NET might help us handle this. Review the class diagram below.

addressClassDiagram

As one can expect, we have created an Address object and used it to represent the MainOfficeAddress property in the Vendor and Customer classes respectively. IBatis lets us do this as well. First we create a new map for our Address object (don’t forget to add a type alias).

<resultMap id="AddressRM" class="Address">
    <result property="Line1" column="AddressLine1"/>
    <result property="Line2" column="AddressLine2"/>
    <result property="City" column="City"/>
    <result property="Zip" column="PostalCode"/>
</resultMap>

Then we modify our original result maps as below, using our new address result map to eliminate redundancy.

<resultMap id="GetCustomerAndPrimaryAddressesRM"
                     class="Customer">
    <result property="AccountNumber" column="AccountNumber"/>
    <result property="MainOfficeAddress" resultMapping="AdventureWorks.AddressRM"/>
</resultMap>

<resultMap id="GetVendorAndPrimaryAddressesRM"
                     class="Vendor">
    <result property="Name" column="Name"/>
    <result property="MainOfficeAddress" resultMapping="AdventureWorks.AddressRM"/>
</resultMap>

The key here is the new resultMapping attribute. We use it instead of the column attribute to indicate how the corresponding property is getting filled. Note also the “fully qualified name” for the map we are using to fill the MainOfficeAddress property. The first part of the token, AdventureWorks, corresponds to the namespace that is declared at the top of the sql map.

<sqlMap namespace="AdventureWorks"
        xmlns="http://ibatis.apache.org/mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

Note finally that this full qualification is also required for the resultMapping attribute to work properly. The select statements can stay the same. Notice how much cleaner the maps look, and how we are reusing the address map. As we get further along we will start splitting our maps up across files, and it will become even more important to know how to reference maps from other files so we aren’t repeating the same information over and over.

Next Steps

We didn’t get to null handling in this installment, so we’ll be sure to hit that up in Part 4. Also, we’ll go over how to use built-in classes (e.g. ArrayList) to hold query results. Finally, we’ll go over multiple result classes within the same query.

  3 Responses to “IBatis.NET Tips & Tricks Part 3 – Map Composition”

  1. IBatis.NET Tips & Tricks Part 3 – Map Composition…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. Thank you for your post

  3. [...] In MyBatis.NET, this is a way to reuse defined maps in a DRY way; the post can be found here. As usual, we will be building on the code base we’ve build in this series; refer to the last [...]

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

   
© 2011 Musings of the Bare Bones Coder Suffusion theme by Sayontan Sinha