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 |
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.
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.
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.
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
.
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:
We have removed explicit inheritance strategy declarations for classes using the default strategy.
Magazine
,
Article
, and Company
use the none
discriminator strategy
because we do not plan on subclassing them.
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.
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.
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
.
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>