JPOX
JPOX
 Project  |  Ver 1.1  |  Ver 1.2  |  JDO  |  JPA  |  Guides  |  Tools
1.1 | Preparation | O/R Mapping | Runtime | Extensions | Developer
O/R Mapping
Relationships
Inheritance Strategies

In Java it is a normal situation to have inheritance between classes. With JDO you have choices to make as to how you want to persist your classes for the inheritance tree. For each class you select how you want to persist that classes information. You have the following choices.

  1. The first and simplest to understand option is where each class has its own table in the datastore. In JDO2 this is referred to as new-table. JPOX 1.0 handled everything this way.
  2. The next way is to select a class to have its fields persisted in the table of its subclass. In JDO2 this is referred to as subclass-table
  3. The final way is to select a class to have its fields persisted in the table of its superclass. In JDO2 this is known as superclass-table.

In order to demonstrate the various inheritance strategies we need an example. Here are a few simple classes representing products in a (online) store. We have an abstract base class, extending this to to provide something that we can represent any product by. We then provide a few specialisations for typical products. We will use these classes later when defining how to persistent these objects in the different inheritance strategies.



JDO2 imposes a "default" inheritance strategy if none is specified for a class. If the class is a base class and no inheritance strategy is specified then it will be set to new-table for that class. If the class has a superclass and no inheritance strategy is specified then it will be set to superclass-table. This means that, when no strategy is set for the classes in an inheritance tree, they will default to using a single table managed by the base class.

Please be aware of this default when you are specifying the strategy for your inheritance tree since it differs from what JPOX 1.0 used. You can control the "default" strategy chosen by way of a . This is specified by way of a PMF property org.jpox.rdbms.defaultInheritanceStrategy. The default is JDO2 which will give the above JDO 2 default behaviour for all classes that have no strategy specified. The other option is JPOX which will use "new-table" for all classes which have no strategy specified (as per JPOX 1.0)

See also :-

New Table

Here we want to have a separate table for each class. This has the advantage of being the most normalised data definition. It also has the disadvantage of being slower in performance since multiple tables will need to be accessed to retrieve an object of a sub type. Let's try an example using the simplest to understand strategy new-table. We have the classes defined above, and we want to persist our classes each in their own table. We define the Meta-Data for our classes like this

<package name="org.jpox.sample.store">
    <class name="AbstractProduct">
        <inheritance strategy="new-table"/>
        <field name="name">
            <column length="100" jdbc-type="VARCHAR"/>
        </field>
        <field name="description">
            <column length="255" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="Product">
        <inheritance strategy="new-table"/>
        <field name="price"/>
    </class>
    <class name="Book">
        <inheritance strategy="new-table"/>
        <field name="isbn">
            <column length="20" jdbc-type="VARCHAR"/>
        </field>
        <field name="author">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="TravelGuide">
        <inheritance strategy="new-table"/>
        <field name="country">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="CompactDisc">
        <inheritance strategy="new-table"/>
        <field name="artist">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>

We use the inheritance element to define the persistence of the inherited classes.

In the datastore, each class in an inheritance tree is represented in its own datastore table (tables ABSTRACTPRODUCT, PRODUCT, BOOK, TRAVELGUIDE, and COMPACTDISC), with the subclasses tables' having foreign keys between the primary key and the primary key of the superclass' table.



In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted into ABSTRACTPRODUCT, PRODUCT, BOOK, and TRAVELGUIDE.



Subclass table

JPOX 1.1 supports persistence of classes in the tables of subclasses where this is required. This is typically used where you have an abstract base class and it doesn't make sense having a separate table for that class. In our example we have no real interest in having a separate table for the AbstractProduct class. So in this case we change one thing in the Meta-Data quoted above. We now change the definition of AbstractProduct as follows

<class name="AbstractProduct">
    <inheritance strategy="subclass-table"/>
    <field name="name">
        <column length="100" jdbc-type="VARCHAR"/>
    </field>
    <field name="description">
        <column length="255" jdbc-type="VARCHAR"/>
    </field>
</class>

This subtle change of use the inheritance element has the effect of using the PRODUCT table for both the Product and AbstractProduct classes, containing the fields of both classes.

In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted into PRODUCT, BOOK, and TRAVELGUIDE.

Please note that JPOX doesn't currently support the use of classes defined with subclass-table strategy as being either the container end of a 1-N relationship, nor of such classes being the "element-type" in a 1-N relationship..



Superclass table

JPOX 1.1 supports persistence of classes in the tables of superclasses where this is required. This has the advantage that retrieval of an object is a single SQL call to a single table. It also has the disadvantage that the single table can have a very large number of columns, and database readability and performance can suffer, and additionally that a discriminator column is required. In our example, lets ignore the AbstractProduct class for a moment and assume that Product is the base class. We have no real interest in having separate tables for the Book and CompactDisc classes and want everything stored in a single table PRODUCT. We change our MetaData as follows

    <class name="Product">
        <inheritance strategy="new-table">
            <discriminator strategy="class-name">
                <column name="PRODUCT_TYPE"/>
            </discriminator>
        </inheritance>
        <field name="price"/>
    </class>
    <class name="Book">
        <inheritance strategy="superclass-table"/>
        <field name="isbn">
            <column length="20" jdbc-type="VARCHAR"/>
        </field>
        <field name="author">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="TravelGuide">
        <inheritance strategy="superclass-table"/>
        <field name="country">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="CompactDisc">
        <inheritance strategy="superclass-table"/>
        <field name="artist">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column name="DISCTITLE" length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>

This change of use of the inheritance element has the effect of using the PRODUCT table for all classes, containing the fields of Product, Book, CompactDisc, and TravelGuide. You will also note that we used a discriminator element for the Product class. The specification above will result in an extra column (called PRODUCT_TYPE) being added to the PRODUCT table, and containing the class name of the object stored. So for a Book it will have "org.jpox.samples.store.Book" in that column. This column is used in discriminating which row in the database is of which type. The final thing to note is that in our classes Book and CompactDisc we have a field that is identically named. With CompactDisc we have defined that its column will be called DISCTITLE since both of these fields will be persisted into the same table and would have had identical names otherwise - this gets around the problem.



In the above example, when we insert a TravelGuide object into the datastore, a row will be inserted into the PRODUCT table only.



JDO 2 allows two types of discriminators. The example above used a discriminator strategy of class-name. This inserts the class name into the discriminator column so that we know what the class of the object really is. The second option is to use a discriminator strategy of value-map. With this we will define a "value" to be stored in this column for each of our classes. The only thing here is that we have to define the "value" in the MetaData for ALL classes that use that strategy. So to give the equivalent example :-

    <class name="Product">
        <inheritance strategy="new-table">
            <discriminator strategy="value-map" value="PRODUCT">
                <column name="PRODUCT_TYPE"/>
            </discriminator>
        </inheritance>
        <field name="price"/>
    </class>
    <class name="Book">
        <inheritance strategy="superclass-table">
            <discriminator value="BOOK"/>
        </inheritance>
        <field name="isbn">
            <column length="20" jdbc-type="VARCHAR"/>
        </field>
        <field name="author">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="TravelGuide">
        <inheritance strategy="superclass-table">
            <discriminator value="TRAVELGUIDE"/>
        </inheritance>
        <field name="country">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>
    <class name="CompactDisc">
        <inheritance strategy="superclass-table">
            <discriminator value="COMPACTDISC"/>
        </inheritance>
        <field name="artist">
            <column length="40" jdbc-type="VARCHAR"/>
        </field>
        <field name="title">
            <column name="DISCTITLE" length="40" jdbc-type="VARCHAR"/>
        </field>
    </class>

As you can see from the MetaData DTD it is possible to specify the column details for the discriminator. JPOX supports this, but only currently supports the following values of jdbc-type : VARCHAR, CHAR, INTEGER, BIGINT, NUMERIC. The default column type will be a VARCHAR.



Retrieval of inherited objects

JDO provides particular mechanisms for retrieving inheritance trees. These are accessed via the Extent/Query interface. Taking our example above, we can then do

tx.begin();
Extent e = pm.getExtent(org.jpox.samples.store.Product.class, true);
Query  q = pm.newQuery(e);
Collection c=(Collection)q.execute();
tx.commit();

The second parameter passed to pm.getExtent relates to whether to return subclasses. So if we pass in the root of the inheritance tree (Product in our case) we get all objects in this inheritance tree returned. You can, of course, use far more elaborate queries using JDOQL, and SQL but this is just to highlight the method of retrieval of subclasses.