Join-mapping to load data from multiple tables

Components address a situation where multiple domain classes need to be mapped to a single database table. If you have the exact opposite situation, where a single domain entity needs to be mapped to multiple database tables, then you can use joins. This feature comes with a caveat though—the tables being joined must have a one-to-one relation between them. You would soon agree that this caveat makes sense.

To understand this better, we would assume that in our employee benefits domain model, we do not have an Address entity, and all address fields are present on the Employee entity itself. Moreover, we would also assume that in the database, address fields are stored in a different table named Address, having a one-to-one relation with Employee table. For the sake of simplicity, we would ignore other fields on Employee entity. Following is how the Employee entity with details relevant to this discussion looks:

public class Employee
{
  public virtual int Id { get; set; }
  public virtual string AddressLine1 { get; set; }
  public virtual string AddressLine2 { get; set; }
}

We only have an Id field and a couple of more fields for address. We now want to map this entity such that fields related to address would come from a different table. If you are using mapping by code, then a function named Join is available to specify that some of the properties of this entity need to be fetched by joining another table. The following code listing shows this mapping:

public class EmployeeMapping : ClassMapping<Employee>
{
  public EmployeeMapping()
  {
    Id(e => e.Id);
    Join("tableName", joinMapper =>
    {
      joinMapper.Table("Address");
      joinMapper.Key(keyMapper => keyMapper.Column("Id"));
      joinMapper.Property(e => e.AddressLine1);
      joinMapper.Property(e => e.AddressLine2);
    });
  }
}

You are familiar with the most part of the preceding mapping code. The only new part is the call to Join method. Let's drill a bit into that method. The first parameter to this method is a string which is a default table name. I have just passed tableName here, as I intend to override this value later on, as we will see. The second parameter is a delegate that takes NHibernate.Mapping.ByCode.IJoinMapper<T> as its parameter. In this case, it happens to be IJoinMapper<Employee>. IJoinMapper, which has several methods available on it that you can use to control how the Employee and Address tables are joined. We have used three methods from this interface. First one, Table, is used to specify the name of the table to be joined. Second, Key, is used to specify the name of the key column on the table. This information is used by NHibernate internally, when querying the table or inserting records into this table. The last method, Property, is similar to the Property method available during usual class mapping. This method is used to map properties to columns on the table.

We have the following test to verify the behavior of such a mapping:

[Test]
public void AddressIsSavedCorrectly()
{
  object id = 0;
  using (var tx = Session.BeginTransaction())
  {
    id = Session.Save(new Employee
    {
      AddressLine1 = "address line 1",
      AddressLine2 = "address line 2"
    });

    tx.Commit();
  }

  Session.Clear();

  using (var tx = Session.BeginTransaction())
  {
    var employee = Session.Get<Employee>(id);
    Assert.That(employee.AddressLine1, Is.EqualTo("address line 1"));
    tx.Commit();
  }
}

We are inserting an instance of Employee and then retrieving it by its Id. The following SQL statements are generated by NHibernate for the preceding code:

INSERT INTO Employee (Id)
VALUES
(@p0);
@p0 = 0

INSERT INTO Address (AddressLine1, AddressLine2, Id)
VALUES (@p0, @p1, @p2);
@p0 = 'address line 1', @p1 = 'address line 2', @p2 = 0

SELECT     employee0_.Id             AS Id0_0_, 
           employee0_1_.AddressLine1 AS AddressL2_1_0_, 
           employee0_1_.AddressLine2 AS AddressL3_1_0_ 
FROM       Employee employee0_ 
INNER JOIN Address employee0_1_ 
ON         employee0_.Id=employee0_1_.Id 
WHERE      employee0_.Id=@p0;@p0 = 0

This is, as expected. You can notice that NHibernate used an inner join, as that is the default in this case. But if you want, you can change it to an outer join by specifying that the record in Address table is optional.

There is not much to join mapping. You can map properties from an entity to as many as tables as you want. But remember that this is not recommended if you are working on a green field project. This feature has only been made available to work with legacy databases. In an ideal world, it is expected to have a lesser number of tables than the number of entities in the domain model.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.30.210