views:

73

answers:

1

I have a rather large inheritance hierarchy in which some of the subclasses add very little and others add quite a bit. I don't want to map the entire hierarchy using either "table per class hierarchy" or "table per subclass" due to the size and complexity of the hierarchy. Ideally I'd like to mix mapping strategies such that portions of the hierarchy where the subclasses add very little are combined into a common table a la "table per class hierarchy" and subclasses that add a lot are broken out into a separate table. Using this approach, I would expect to have 2 or 3 tables with very little wasted space instead of either 1 table with lots of fields that don't apply to most of the objects, or 20+ tables, several of which would have only a couple of columns.

In the NHibernate Reference Documentation version 2.1.0, I found section 8.1.4 "Mixing table per class hierarchy with table per subclass". This approach switches strategies partway down the hierarchy by using:

...
<subclass ...>
    <join ...>
        <property ...>
        ...
    </join>
</subclass>
...

This is great in theory. In practice, though, I found that the schema was too restrictive in what was allowed inside the "join" element for me to be able to accomplish what I needed.

Here is the related part of the schema definition:

<xs:element name="join">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="subselect" minOccurs="0" />
            <xs:element ref="comment" minOccurs="0" />
            <xs:element ref="key" />
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element ref="property" />
                <xs:element ref="many-to-one" />
                <xs:element ref="component" />
                <xs:element ref="dynamic-component" />
                <xs:element ref="any" />
                <xs:element ref="map" />
                <xs:element ref="set" />
                <xs:element ref="list" />
                <xs:element ref="bag" />
                <xs:element ref="idbag" />
                <xs:element ref="array" />
                <xs:element ref="primitive-array" />
            </xs:choice>
            <xs:element ref="sql-insert" minOccurs="0" />
            <xs:element ref="sql-update" minOccurs="0" />
            <xs:element ref="sql-delete" minOccurs="0" />
        </xs:sequence>
        <xs:attribute name="table" use="required" type="xs:string" />
        <xs:attribute name="schema" type="xs:string" />
        <xs:attribute name="catalog" type="xs:string" />
        <xs:attribute name="subselect" type="xs:string" />
        <xs:attribute name="fetch" default="join">
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="join" />
                    <xs:enumeration value="select" />
                </xs:restriction>
            </xs:simpleType>
        </xs:attribute>
        <xs:attribute name="inverse" default="false" type="xs:boolean">
        </xs:attribute>
        <xs:attribute name="optional" default="false" type="xs:boolean">
        </xs:attribute>
    </xs:complexType>
</xs:element>

As you can see, this allows the use of "property" child elements or "component" child elements, but not both. It also doesn't allow for "subclass" child elements to continue the hierarchy below the point at which the strategy was changed.

Is there a way to accomplish this?

A: 

You have probably misread that schema. It clearly shows that you can use as many components, properties and many-to-ones as you want.

And no, it's not possible to add more subclassing strategies inside it.

Diego Mijelshon
<xs:choice minOccurs="0" maxOccurs="unbounded">...If I understand it correctly, "choice" means that only one of the children contained in the choice element can be used. MaxOccurs="unbounded" means that the one choice can be repeated as many times as you like. So you could have 50 properties, but you can't have both properties and components. I checked a couple of xsd references and this appears to be what they are saying.
MylesRip
I just ran some more tests and I get errors even when I use only one type so maybe the problem goes deeper. Here is the error I get when I include only properties: "The element 'join' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'property' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'subselect, comment, key' in namespace 'urn:nhibernate-mapping-2.2'."Hmm...
MylesRip
You get that error because you are missing the key. And your previous interpretation of the choice is not correct: you have "unbounded" choices; if you wanted many instances of the same element you'd have to place the MaxOccurs="unbounded" in the element, not in the <choice>
Diego Mijelshon
Thanks Diego. You are correct. Adding the key got rid of the error - even when I use both properties and components. Unfortunately, since it's not possible to add more subclasses inside the joined table, this approach only works for breaking out the final layer in the hierarchy, which doesn't help me. I've had to abandon the approach and will probably go with a single table for the entire class hierarchy. This still allows the domain model to be fine-grained. It also means that I don't have to create attrocious views for reporting engines that want to go directly against the database. :)
MylesRip