Continuing in our series of comparisons between NHibernate and MyBatis.NET, we will reprise our take on simple type conversions and type handlers from an NHibernate perspective, comparing how said functionality stands up to what is offered in MyBatis.NET.
The prior post that covered type conversions and type handlers in MyBatis.NET can be found here. As in that post, we will be using the Microsoft AdventureWorks database.
Automatic Conversions
Quickly reviewing the table structure we modified for our MyBatis example, we have the below schema for the HumanResources.Department table.
Remember that the gimmick here is that we have a text field that contains either “true” or “false”. The object that we want to hydrate from this table is pictured below.
We see immediately that the property is of type boolean. How then can we hydrate this using NHibernate?
As usual, we start by adding a mapping called Department.hbm.xml to our Mappings folder in our project.
The actual mapping is given below.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="NHAdventureWorksDAL.HumanResources.Department,NHAdventureWorksDAL"
table="HumanResources.Department">
<id name="Id" column="DepartmentID" />
<property name="TextualBoolean" />
</class>
</hibernate-mapping>
Once the mapping is in place, we write our test.
[Test]
public void CanGetDepartmentByIdTest()
{
HumanResourcesRepo hr = new HumanResourcesRepo();
Department d = hr.GetDepartmentById(1);
Assert.That(d, Is.Not.Null);
Assert.That(d.TextualBoolean, Is.True);
}
Notice that we’ve introduced a HumanResourcesRepo class, to serve as our repository interface for the HumanResources data. The test is simple enough. The implementation of the GetDepartmentById is below.
public Department GetDepartmentById(int departmentId)
{
using (ISession session = AWHelper.OpenSession())
{
return session.Get<Department>(departmentId);
}
}
That’s it. With that code, our test passes. Notice that we didn’t have to write SQL as in our MyBatis example.
Custom Type Conversions
Our next type conversion had to do with the Employee table. Remember that there we had a marital status column which contained either “M” for married or “S” for single.
The object we want to hydrate appears below.
As you can see, the Employee class represents marital status via an enumeration. We need some way to tell NHibernate that M should map to Married and S should map to Single. How to do this?
As in the MyBatis case, we start by creating a new class to handle the special column. This new class inherits from NHibernate.Type.EnumStringType, and the initial shell appears below.
using System;
using NHibernate.Type;
using NHAdventureWorksDAL.Person;
namespace NHAdventureWorksDAL.Helpers
{
public class MaritalStatusHandler : EnumStringType
{
protected MaritalStatusHandler()
: base(typeof (MaritalStatus))
{
}
public override object GetValue(object code)
{
return base.GetValue(code);
}
public override object GetInstance(object code)
{
return base.GetInstance(code);
}
}
}
The first thing to note here is that we are overriding the GetValue and GetInstance methods. There are other methods, but these are the only two that we need to override. Also note the constructor; we are telling NHibernate which enumeration we are dealing with by passing the type to the base class constructor.
To actually get this to do something, we have to write some code in the GetValue and GetInstance methods (don’t forget to set the constructor to public as well). The GetValue method takes an enumeration and returns a string that should be persisted to the data store; the GetInstance method does the reverse. The code to actually accomplish this based on our example appears below.
public override object GetValue(object code)
{
if (code == null)
{
return String.Empty;
}
else
{
switch ((MaritalStatus)code)
{
case MaritalStatus.Married:
return "M";
case MaritalStatus.Single:
return "S";
default:
throw new ApplicationException("Invalid marital status.");
}
}
}
public override object GetInstance(object code)
{
switch (code.ToString().ToUpper())
{
case "S":
return MaritalStatus.Single;
case "M":
return MaritalStatus.Married;
default:
string msg = string.Format("Unknown marital status {0} encountered.", code.ToString());
throw new ApplicationException(msg);
}
}
Nothing crazy going on here, just some simple conversion code. We got ahead of ourselves and wrote code without a test to exercise it, so let’s get back to that. We’ll toss the responsibility of fetching an Employee into our Person repository; messy, but it’ll do for now.
[Test]
public void CanGetEmployeeByIdTest()
{
PersonRepo pr = new PersonRepo();
Employee e = pr.GetEmployeeById(1);
Assert.That(e, Is.Not.Null);
Assert.That(e.CurrentMaritalStatus, Is.EqualTo(MaritalStatus.Married));
}
A simple enough data-dependent test that requires us to augment the PersonRepo class with a new method, requires an Employee class, and requires a MaritalStatus enumeration.
public Employee GetEmployeeById(int employeeId)
{
using (ISession session = AWHelper.OpenSession())
{
return session.Get<Employee>(employeeId);
}
}
The repository method is trivial, as are the Employee class and MaritalStatus enumeration which are pictured below.
public enum MaritalStatus
{
Single,
Married
}
public class Employee
{
public virtual int Id { get; set; }
public virtual MaritalStatus CurrentMaritalStatus { get; set; }
}
After all of this, we are missing one final piece: the mapping.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="NHAdventureWorksDAL.Person.Employee,NHAdventureWorksDAL"
table="HumanResources.Employee">
<id name="Id" column="EmployeeId" />
<property name="CurrentMaritalStatus"
column="MaritalStatus"
type="NHAdventureWorksDAL.Helpers.MaritalStatusHandler,NHAdventureWorksDAL" />
</class>
</hibernate-mapping>
Notice that for the MaritalStatus property, we have specified our marital status handler class as the type. This tells NHIbernate what class it needs to use to interpret column values for this property. And with this, our test passes.
Conclusions
The handling of type conversions in NHibernate is very similar to that of MyBatis.NET, albeit without SQL living in XML files. That alone is starting to bring NHibernate up to par at least, if not allowing it to pull ahead. As far as the rest of the setup for the above scenarios, the effort was very similar to that which was required with MyBatis.NET.
NHibernate is definitely finding it’s steam.
[...] NHibernate vs. MyBatis.NET – Type Conversions & Type Handlers | Musings of the Bare Bones… [...]
NHibernate vs. MyBatis.NET – Type Conversions & Type Handlers…
Thank you for submitting this cool story – Trackback from DotNetShoutout…