My team’s usage of IBatis.NET continues to meet success. While we miss the sugary goodness that is SubSonic’s auto-generated ActiveRecord code, we are starting to appreciate the newfound flexibility that IBatis.NET affords us when it comes to handling the impedance mismatch between our domain and our databases. Because of this, I’ve taken some time to spelunk through IBatis.NET’s unit tests to see what additional functionality might be available that we aren’t taking advantage of. Given the sparse documentation for the tool, this has become the best way to find out how to fully leverage IBatis.NET in our environment.
I’ll assume that you have successfully set your environment up to work with IBatis.NET. If not, you can do so by reading my post here. All of the examples hit the AdventureWorks database.
Mapping to Constructors
This was a neat little gem I found by checking out the Account map in the DataMapper test project. Let’s say you have an object defined as below.
Notice that the first and last name properties are read only properties, and must be hydrated via the constructor. IBatis.NET can hydrate the Person object by mapping results to the corresponding constructor arguments. First, the map.
<resultMap id="SelectPersonWithConstructorRM"
class="Person">
<constructor>
<argument argumentName="firstName" column="FirstName"/>
<argument argumentName="lastName" column="LastName"/>
</constructor>
</resultMap>
Notice the constructor tag and its children, the argument tags. All these tags do is tell IBatis.NET to map the query results named FirstName and LastName to the corresponding constructor parameters on the Person class. The select statement here is trivial.
<select id="SelectPersonWithConstructor"
parameterClass="integer"
resultMap="SelectPersonWithConstructorRM">
select
*
from
Person.Contact
where
ContactID = #value#
</select>
The code to call this statement and its corresponding result map is the same as just about all other IBatis.NET code.
Person p = Mappers.AWMapper.QueryForObject<Person>("SelectPersonWithConstructor", 1);
I’m using the standard static Mapper helper class approach, with my AdventureWorks mapper being named AWMapper.
Hydrating Child Objects From Joins
Now let’s say we have an object hierarchy defined as below.
So we have a Product class that contains a ProductModel class. Our database schema follows along the same lines, with a Product table and a Model table. In our scenario, we want to hydrate the entire Product object at once. To accomplish this, IBatis.NET provides us with a resultMapping property that we can use in lieu of the column property on a result element. The resultMapping property lets us basically compose result maps, reducing duplication and minimizing our required XML file jiujutsu. First the map.
<resultMap id="ProductWithModelRM"
class="Product">
<result property="ProductId" column="ProductID" />
<result property="ProductName" column="ProductName"/>
<!-- resultMappings have to be fully qualified -->
<result property="Model" resultMapping="AdventureWorks.ProductModelRM"/>
</resultMap>
You can see how the resultMapping attribute is applied to a result element above. You’ll notice that the value of the resultMapping attribute is “AdventureWorks.ProductModelRM.” This is because the map you reference in a resultMapping attribute must by fully qualified; that is, you must prefix the result map name with the namespace of the sqlMap that the result map is contained in, even if it’s the same file. For completeness, we show the namespace declaration for the file the above result map is contained in as well as the ProductModelRM result map we are referencing.
<sqlMap namespace="AdventureWorks"
xmlns="http://ibatis.apache.org/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<resultMap id="ProductModelRM"
class="ProductModel">
<result property="ModelId" column="ModelId"/>
<result property="ModelName" column="ModelName"/>
</resultMap>
Finally, the code that uses this map, which is again trivial.
Product p = Mappers.AWMapper.QueryForObject<Product>("SelectProductAndModel", 784);
Cool stuff, and very straightforward once you get the hang of it.
Next Steps
This is barely scratching the surface of the knowledge that can be gleaned from IBatis.NET’s unit tests. In the next part of this series I’ll show how we can convert database types into different managed types, and how to work with the typeHandler attribute on the result element in a result map.
IBatis.NET Tips & Tricks Part 1 – Constructors & Joins…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
[...] you can handle type mismatches between your database and your objects. We build on the code from Part 1 of this series, so start there if you want to be fully up to [...]
[...] IBatis.NET Tips & Tricks 1 [...]
Please provide sample code.how to create domain layer?