15.9. Discriminator

15.9.1. class-name
15.9.2. value-map
15.9.3. none
15.9.4. Putting it All Together

The superclass-table inheritance strategy results in a single table containing records for two or more different classes in an inheritance hierarchy. Similarly, using the new-table strategy to create joined subclass tables results in the superclass table holding records for superclass instances as well as for the superclass state of subclass instances. When selecting data, JDOR needs a way to differentiate a row representing an object of one class from a row representing an object of another. That is the job of the discriminator mapping element.

The discriminator element is nested within the inheritance element. The least-derived mapped class in an inheritance hierarchy defines the discriminator. In other words, the first new-table class in a hierarchy declares the discriminator strategy and column; subclass-table superclasses do not declare a discriminator at all, and all subclasses of the first new-table class can only declare a discriminator value. The discriminator element has the following attributes:

15.9.1. class-name

The class-name discriminator strategy writes the name of the class each row represents to the discriminator column, indicated by the column attribute/element. Aside from its ease-of-use, the main advantage of this strategy is that a good JDOR implementation can calculate all of the subclasses of a given persistent type solely by querying the discriminator column. This means that you do not have to point the implementation at a manually-maintained list of persistent classes. It can be bothersome to keep such a list properly synchronized with a rapidly-changing object model during development, or to deploy with a full list of classes in a J2EE environment where multiple sub-applications share a single server.

On the other hand, storing class names in the database couples your data to your Java object model. In many projects, the persistent data will last far longer than your object mode; perhaps even longer than the Java language itself! For these projects, consider the value-map strategy.

15.9.2. value-map

The value-map strategy is like the class-name strategy, except that a designated symbolic value is written to the discriminator column instead of the class name. This means the JDOR implementation cannot determine a type's subclasses by querying the discriminator column alone, but it has the advantage of keeping your database independent of your object model.

To use the value-map strategy, the least-derived mapped class declares the discriminator column, and all concrete classes in the hierarchy (least-derived class included) use the discriminator element's value attribute to assign themselves a symbolic value. This value will be inserted into the discriminator column for rows representing instances of the class. In fact, the discriminator strategy automatically defaults to value-map whenever the value attribute is defined; you do not need to specify the strategy attribute as well.

15.9.3. none

Setting the discriminator strategy to none indicates that there is no discriminator column. This is illegal for hierarchies using superclass-table inheritance, but is valid in other circumstances:

  • Base classes that you will never extend do not need a discriminator.

  • Subclasses of a subclass-table type do not need a discriminator, unless they themselves have additional subclasses.

  • A vertical hierarchy that uses joined subclass tables via the new-table inheritance strategy does not require a discriminator column, if your database supports outer joins. An outer join is a join in which the related record does not have to exist. When reading an object, the JDOR implementation can outer join to all possible subclass tables and use the presence or absence of a matching row in each table to calculate the class of the instance. Obviously, this is not a very efficient strategy for large hierarchies; for these we recommend using a column-based strategy such as value-map.

15.9.4. Putting it All Together

We can now translate our newfound knowledge of JDOR discriminators into concrete JDOR mappings. We first extend our diagram with discriminator columns:

Next, we present the updated mapping document. There are several things to notice in this revised version:

  1. We have removed explicit inheritance strategy declarations for classes using the default strategy.

  2. Magazine, Article, and Company use the none discriminator strategy because we do not plan on subclassing them.

  3. Author uses the class-name strategy; perhaps we plan on selling our application framework to other developers, and we want them to be able to add new subclasses of Author easily.

  4. Contract is abstract and is not a mapped class (it uses subclass-table inheritance), and therefore it has no discriminator. Its two subclasses Subscription and LineItem, however, are each the least-derived mapped classes in the hierarchy, and so they each declare a discriminator. Note that though they are sibling classes, they can each use a different discriminator mapping because their superclass is unmapped.

  5. Subscription's discriminator strategy automatically defaults to value-map because we define the value attribute. Under the value-map strategy, we must also define a unique discriminator value for every concrete subclass of Subscription.

  6. Discriminator columns default to type VARCHAR. Subscription and its subclasses use numeric discriminator values; therefore, when we map Subscription's discriminator column we use a nested column element rather than the column attribute. The nested element allows us to tell the JDOR implementation the proper jdbc-type of the column.

Example 15.9. Discriminator Mapping

<?xml version="1.0"?>
<orm>
    <package name="org.mag">
        <sequence name="ArticleSeq" datastore-sequence="ART_SEQ"/> 
        <class name="Magazine" table="MAG">
            <inheritance>
                <discriminator strategy="none"/>
            </inheritance>
            <field name="isbn">
                <column name="ISBN" jdbc-type="char" length="15"/>
            </field>
            <field name="title" column="TITLE"/>
            ...
        </class>
        <class name="Article" table="ART">
            <inheritance>
                <discriminator strategy="none"/>
            </inheritance>
            <field name="id" column="ID"/>
            ...
        </class>    
    </package>
    <package name="org.mag.pub">
        <sequence name="AuthorSeq" factory-class="Author$SequenceFactory"/>
        <class name="Company" table="COMP">
            <datastore-identity column="CID" strategy="autoassign"/>
            <inheritance>
                <discriminator strategy="none"/>
            </inheritance>
            ...
        </class>    
        <class name="Author" table="AUTH">
            <datastore-identity sequence="AuthorSeq">
                <column name="AID" sql-type="INTEGER64"/>
            </datastore-identity>
            <inheritance>
                <discriminator column="CLS" strategy="class-name"/>
            </inheritance>
            ...
        </class>
    </package>
    <package name="org.mag.subscribe">
        <sequence name="ContractSeq" strategy="transactional"/> 
        <class name="Contract">
            <inheritance strategy="subclass-table"/>
            ...
        </class>
        <class name="Subscription" table="CNTRCT.SUB">
            <inheritance>
                <discriminator value="1">
                    <column name="TYPE" jdbc-type="tinyint"/>
                </discriminator>
            </inheritance>
            <field name="Contract.id" column="ID"/>
            ...
        </class>
        <class name="LifetimeSubscription">
            <inheritance>
                <discriminator value="2"/>
            </inheritance>
            ...
        </class>
        <class name="TrialSubscription" table="CNTRCT.TRIAL_SUB">
            <inheritance strategy="new-table">
                <join column="ID"/>
                <discriminator value="3"/>
            </inheritance>
            ...
        </class>
        <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
            <inheritance>
                <discriminator strategy="none"/>
            </inheritance>
            <field name="Contract.id" column="ID"/>
            ...
        </class>
    </package>
</orm>

 

Skip navigation bar   Back to Top