Creating class hierarchy mappings

It's common to have an inheritance hierarchy of subclasses. In this example, I will show you one method for mapping inheritance with NHibernate, called table-per-class hierarchy.

Getting ready

Complete the previous Mapping a class with XML example.

How to do it...

  1. Create a new class named Book with the following code:
    namespace Eg.Core
    {
      public class Book : Product
      {
    
        public virtual string ISBN { get; set; }
        public virtual string Author { get; set; }
    
      }
    }
  2. Create a new class named Movie with the following code:
    namespace Eg.Core
    {
      public class Movie : Product 
      {
    
        public virtual string Director { get; set; }
    
      }
    }
  3. Change the Product mapping to match the XML shown in the following code:
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Eg.Core"
        namespace="Eg.Core">
      <class name="Product">
        <id name="Id">
          <generator class="guid.comb" />
        </id>
        <discriminator column="ProductType" />
        <natural-id mutable="true">
          <property name="Name" not-null="true" />
        </natural-id>
        <property name="Description" />
        <property name="UnitPrice" not-null="true" />
      </class>
    </hibernate-mapping>
  4. Create a new embedded resource named Book.hbm.xml with the following XML:
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Eg.Core"
        namespace="Eg.Core">
      <subclass name="Book" extends="Product">
        <property name="Author"/>
        <property name="ISBN"/>
      </subclass>
    </hibernate-mapping>
  5. Create another embedded resource named Movie.hbm.xml with the next XML:
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="Eg.Core"
        namespace="Eg.Core">
      <subclass name="Movie" extends="Product">
        <property name="Director" />
      </subclass>
    </hibernate-mapping>

How it works...

In this example, we've mapped a table-per-class hierarchy, meaning data for our entire hierarchy is stored in a single table, as shown in the next screenshot:

How it works...

NHibernate uses a discriminator column, ProductType in this case, to distinguish among products, books, and movies. By default, the discriminator contains the class name. In this example, that would be Eg.Core.Product, Eg.Core.Book, or Eg.Core.Movie. These defaults can be overridden in the mappings by using a discriminator-value attribute on our class and subclass elements.

In our Book.hbm.xml mapping, we've defined Book as a subclass of Product with Author and ISBN properties. In our Movie.hbm.xml mapping, we've defined Movie as a subclass of Product with a Director property.

With table-per-class-hierarchy, we cannot define any of our subclass properties as not-null="true", because this would create a not-null constraint on those fields. For instance, if we set up the Director property as not null, we wouldn't be able to insert Product or Book instances, because they don't define a Director property. If this is required, use one of the hierarchy mapping strategies listed next.

There's more...

Java refugees may recognize the extends attribute, as extends is the Java keyword used to declare class inheritance. NHibernate first came to life as a port of Java's Hibernate ORM.

Table-per-class hierarchy is the suggested method for mapping class hierarchies, but NHibernate always gives us other options. However, mixing these options within the same class hierarchy is discouraged, and only works in very limited circumstances.

Table per class

In table-per-class mappings, properties of the base class (Product) are stored in a shared table, while each subclass gets its own table for the subclass properties.

Table per class

Table per subclass uses the joined-subclass element, which requires a key element to name the primary key column. As the name implies, NHibernate will use a join to query for this data. Also, notice that our Product table doesn't contain a ProductType column. Only table-per-class hierarchy uses discriminators. Using table-per-class, our Movie mapping will appear as the following code:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="Eg.Core"
    namespace="Eg.Core">
  <joined-subclass name="Movie" extends="Product">
    <key column="Id" />
    <property name="Director" />
  </joined-subclass>
</hibernate-mapping>

Table per concrete class

In table-per-concrete-class mappings, each class gets its own table containing columns for all properties of the class and the base class, as shown in the next screenshot:

Table per concrete class

There is no duplication of data. That is, data from a Book instance is only written to the Book table, not the Product table. To fetch Product data, NHibernate will use unions to query all three tables. Using table-per-concrete-class, our Movie mapping will appear as shown in the following code:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="Eg.Core"
    namespace="Eg.Core">
  <union-subclass name="Movie" extends="Product">
    <property name="Director" />
  </union-subclass>
</hibernate-mapping>

See also

  • Mapping a class with XML
  • Mapping a one-to-many relationship
  • Setting up a base entity class
  • Handling versioning and concurrency
  • Creating mappings fluently
  • Mapping with ConfORM
..................Content has been hidden....................

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