15.9. Discriminator
Prev | Chapter 15. Mapping Metadata | Next

## 15.9. Discriminator

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:

- `strategy`: The discriminator strategy. JDOR recognizes several standard strategies, which we detail in the following sections. Vendors may also define their own proprietary strategies.

  | Note
  Kodo defines the `final` discriminator strategy for base persistent classes that are not declared final, but nevertheless will not have any persistent subclasses. Using the `final` strategy for these classes is slightly more efficient than the `none` strategy described below. Kodo also allows you to plug in your own discriminator implementations. The Reference Guide's Section 7.10, “Custom Mappings” has details on creating custom discriminators.

- `value`: A discriminator value for this class. This attribute is used with the `value-map` discriminator strategy; see Section 15.9.2, “value-map”.

- `column`: The column that holds the discriminator value. As you will see, not all discriminator strategies require a column. As with the `datastore-identity` element, you can use a nested `column` element in lieu of the `column` attribute.

### 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>

Prev | Up | Next
15.8. Inheritance | Home | 15.10. Version