Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Inheritance
Programmer's Guide > OpenAccess ORM Classic (Old API) > Programming With OpenAccess > Mapping > Classes > Inheritance

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

Classes in an inheritance hierarchy can be mapped to the same table, i.e., (flat mapping) or to different tables, i.e., (vertical mapping and horizontal mapping) or a combination of two strategies, i.e, mixed flat and vertical. In case of flat and vertical mapping, each class indicates how it is mapped to its superclass. Horizontal mapping can be specified only for the topmost class in a hierarchy and those settings are applied to its immediate subclasses.

Flat mapping requires the addition of a discriminator or indicator column to the table for the base class to identify the type of each row (except for one special case, which has been described below). The default name of the discriminator column is voa_class. This column is optional for vertical mapping and not required for horizontal mapping.

The discriminator column is normally mapped to a SQL INTEGER, if all of the discriminator values are int values. But, if any one class in the hierarchy has a discriminator value with type different from integer then the discriminator column will be mapped to SQL VARCHAR. This can however, be changed using the db-class-id extension in the metadata (see db-class-id). The default discriminator value for each class is a 32-bit positive hash of the fully qualified class name. You can also change this using the db-class-id extension.

The examples in the sections below use the following class hierarchy:

Flat Mapping

With flat mapping, fields from the subclasses are mapped to the superclass table. Flat mapping is the simplest and is usually the fastest option so it is the default.

Advantages of flat mapping are:

 

Only a single query is required to retrieve all of the instances in a hierarchy without any extra joins

 

Creating, updating or deleting an instance requires only a single INSERT, UPDATE or DELETE statement

Disadvantages of flat mapping are:

 

The base table has many columns that may be used by only a few subclass instances

 

A discriminator or class indicator column is required to identify the type of each row (except for one special case which has been described below)

Since this is the default mapping, there is no XML mapping information needed. If you wish to supply information in the XML metadata for flat mapping, the entries would be as follows:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Cat" />
   
<class name="Dog" />
   
<class name="Pet" />
   
<class name="Rottweiler" />
   
<class name="WienerDog" />
 
</namespace>
</
mapping>

The corresponding tables for the sample class hierarchy, with flat mapping is shown below:

When only certain subclasses are required, the generated SQL uses the discriminator column to filter the results. Here is an example:

OQL Copy Code
SELECT * FROM DogExtent

The values in the WHERE clause IN list are the default db-class-id values for Dog, WeinerDog and Rottweiler:

SQL Copy Code
SELECT pet_id, voa_class, best_friend, nme, voa_version,
        cats_eaten, lngth
  FROM pet
    WHERE voa_class IN (759263421,1302943882,958541839)

Flat Mapping with No Discriminator

Normally a discriminator column is required for flat inheritance. However if each class in the hierarchy has at the most one subclass, then the discriminator can be disabled and all the rows are considered instances of the leaf class of the hierarchy. A UserException is thrown on any attempt to persist a non-leaf-class instance. Queries, navigation and GetObjectById() calls always return instances of the leaf class.

Vertical Mapping

With vertical mapping each class has its own table containing only its fields.

Advantages of vertical mapping are:

 

Tables are normalized as they only contain columns for the fields of the class they are for and rows are only present for instances of that class

 

New subclasses can be added without changing existing tables

 

A discriminator column is not required. This may help when mapping an inheritance hierarchy to existing tables.

Disadvantages of vertical mapping are:

 

Joins are used to fetch fields from possible subclasses in a single query. If the type of the instance being fetched is known then only its tables are joined, otherwise, all possible subclass tables must be included.

 

Creating, updating, or deleting an instance requires multiple INSERT, UPDATE or DELETE statements (one for each table involved)

To enable vertical mapping for a subclass, just change the inheritance strategy to "vertical" as shown in the following XML metadata example:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Cat">
     
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="Dog">
     
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="Pet" />
   
<class name="Rottweiler">
           
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="WienerDog">
           
<extension key="db-inheritance" value="vertical" />
   
</class>
 
</namespace>
</
mapping>

The corresponding tables for the sample class hierarchy, with vertical mapping is shown below:

The special value {no} is used for the db-class-id in the metadata to disable the discriminator column, as shown in the following XML metadata example:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Pet">
     
<extension key="db-class-id" value="{no}" />
   
</class>
 
</namespace>
</
mapping>

The SQL to fetch all of the pets for a vertical inheritance mapping without a discriminator column is shown below. All tables are pulled in with OUTER joins and the null/not-null status of each of their primary keys is used to determine the type of the row. Any number of instances of different types can be retrieved with a single query. Some O/R mapping tools even run an extra query for each row returned to discover its true type (N+1 queries) or run a separate query against each possible table and merge the results in memory:

SQL Copy Code
SELECT a.pet_id, b.pet_id, c.pet_id, d.pet_id, e.pet_id,
       a.nme, a.voa_version, b.lives_left, c.best_friend,
       d.cats_eaten, e.lngth
  FROM pet a
       LEFT JOIN cat AS b ON (a.pet_id = b.pet_id)
       LEFT JOIN dog AS c ON (a.pet_id = c.pet_id)
       LEFT JOIN rottweiler AS d ON (c.pet_id = d.pet_id)
       LEFT JOIN wiener_dog AS e ON (c.pet_id = e.pet_id)

If a discriminator column is used the SQL is as follows:

SQL Copy Code
SELECT a.pet_id, a.voa_class, a.nme, a.voa_version,
       b.lives_left, c.best_friend, d.cats_eaten, e.lngth
  FROM pet a
       LEFT JOIN cat AS b ON (a.pet_id = b.pet_id)
       LEFT JOIN dog AS c ON (a.pet_id = c.pet_id)
       LEFT JOIN rottweiler AS d ON (c.pet_id = d.pet_id)
       LEFT JOIN wiener_dog AS e ON (c.pet_id = e.pet_id)

If all the instances returned are known to be of a given subclass, OpenAccess ORM will optimize the joins as shown in this example:

OQL Copy Code
SELECT * FROM DogExtent

Note that an INNER join is used to pickup the base class fields:

SQL Copy Code
SELECT a.pet_id, a.pet_id, c.pet_id, d.pet_id,
       a.best_friend, b.nme, b.voa_version, c.cats_eaten,
       d.lngth
  FROM dog a
       INNER JOIN pet AS b ON (a.pet_id = b.pet_id)
       LEFT JOIN rottweiler AS c ON (a.pet_id = c.pet_id)
       LEFT JOIN wiener_dog AS d ON (a.pet_id = d.pet_id)

If the subclass is a leaf class then all the joins will be INNER joins:

OQL Copy Code
SELECT * FROM RottweilerExtent

 

SQL Copy Code
SELECT a.pet_id, a.cats_eaten, b.best_friend, c.nme, c.voa_version
  FROM rottweiler a
       INNER JOIN dog AS b ON (a.pet_id = b.pet_id)
       INNER JOIN pet AS c ON (a.pet_id = c.pet_id)

Mixed Flat and Vertical

OpenAccess ORM supports mixing of flat and vertical mappings in the same hierarchy. For example, in order to save database space, you could choose to use vertical mapping only for seldom-used subclasses with many large fields. To enable vertical mapping for a subclass, just change the inheritance strategy to "vertical" as shown in the following XML metadata example:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Cat">
     
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="Dog">
     
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="Pet" />
   
<class name="Rottweiler" />
   
<class name="WienerDog" />
 
</namespace>
</
mapping>

If both Dog and Cat are changed to vertical with WeinerDog and Rottweiler left flat, the hierarchy will be mapped to three database tables as shown below. A discriminator column (voa_class) is added to the base table to distinguish between Dog, WeinerDog and Rottweiler.

Horizontal Mapping

Horizontal inheritance can only be enabled for the topmost class in a hierarchy. Each immediate subclass is stored in its own table with a "copy" of the fields from the superclass. A horizontally mapped class, i.e. the superclass, does not itself have a table. Therefore, a horizontally mapped class cannot be directly persisted; only subclasses of the class can be stored in the database. For this reason, it is recommended (but not required) that horizontally mapped classes be declared abstract.

While using single or multiple field identity, it is required that the horizontally mapped class is declared abstract.

In case of horizontal mapping, the identity and version information is specified at the next derived class, and if the IdentityField and the VersionField specified are fields that belong to the base class, those fields have to be accessible from the derived classes. Therefore, declaring these fields as "Protected" might be the best way, since if these fields are declared as "Private" the enhanced code will not be able to access these fields. The enhancer will check this and report an error.

Each subclass may map the fields inherited from the superclass differently. Each subclass may have its own settings for identity type, objectid-class, key generator and optimistic locking just like a top level class. Classes further down the hierarchy may be mapped with any combination of flat and vertical inheritance.

To enable horizontal mapping, just change the inheritance strategy to "horizontal" for the base class as shown in the following XML metadata example:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Pet">
      
<extension key="db-inheritance" value="horizontal" />
   
</class>
   
<class name="Cat" />
   
<class name="Dog" />
   
<class name="Rottweiler">
      
<extension key="db-inheritance" value="vertical" />
   
</class>
   
<class name="WienerDog">
      
<extension key="db-inheritance" value="vertical" />
   
</class>
 
</namespace>
</
mapping>

WeinerDog and Rottweiler have been mapped "vertically".

The corresponding tables for the sample class hierarchy, with horizontal mapping is shown below:

Advantages of horizontal mapping are:

 

Persisting and modifying instances of subclasses whose superclass uses horizontal mapping is as fast as flat inheritance mapping, since generally only a single INSERT, DELELTE or UPDATE statement will need to be issued

 

Fetching a single instance will not require any joins

 

Attributes that are common to multiple persistent classes can just be defined in the superclass; this increases the performance and relational design restrictions of using a vertical mapping are reduced

Disadvantages of horizontal mapping are:

 

The base class, in this case, is not really handled like other persistent classes but only its members are inherited into its derived classes

 

Each class derived from the horizontally mapped base class starts a new hierarchy. This is especially relevant while using single or multiple identity, as in this case, the identity must be defined in the derived classes and not in the base class. Also each directly derived class must declare its own identity type, because identity types are not sharable across hierarchies.

 

Querying the topmost class in the hierarchy is expensive as it will require multiple SELECT statements, one for each concrete subclass, to be executed against the database; Querying subclasses is just as efficient as flat mapping

 

References to and collections and maps of the topmost class are not supported

 

From a relational design standpoint, horizontal mappings are not normalized, since attributes are repeated across different tables

To enable horizontal mapping just change the inheritance strategy to "horizontal" as shown in the following XML metadata example:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Pet">
     
<extension key="db-inheritance" value="horizontal" />
   
</class>
   
<class name="Cat" />
   
<class name="Dog" />
   
<class name="Rottweiler" />
   
<class name="WienerDog" />
 
</namespace>
</
mapping>

You can also override the metadata of the superclass within the subclasses, as shown in the following XML metadata example:

Copy Code
<mapping>
<namespace name="Inheritance.Model">
 
<class name="Pet">
  
<extension key="db-inheritance" value="horizontal" />
  
<field name="categoryName">
   
<extension key="db-column">
    
<extension key="db-type" value="VARCHAR" />
    
<extension key="db-column-name" value="CategoryName" />
   
</extension>
  
</field>
 
</class>
 
<class name="Cat" persistence-capable-superclass="Pet">
  
<field name="Pet.categoryName">
   
<extension key="db-column">
    
<extension key="db-type" value="LONGVARCHAR" />
    
<extension key="db-column-name" value="CategoryName" />
   
</extension>
  
</field>
 
</class>
</namespace>
</
mapping>

Discriminator Column

The discriminator column used to identify the type of each row with flat mapping (and, optionally, for vertical mapping) is normally mapped to a SQL INTEGER but can be changed using the db-class-id extension. The default discriminator value for a class is a 32-bit positive hash of the fully qualified class name but you can also change this using the db-class-id extension.

If you are using the default discriminator values, you need to be careful if you change the name or namespace of a class in a hierarchy. The automatically generated values used in the discriminator column will change and this will invalidate the existing data. You can either specify the old class-id as shown in the following example (See db-class-id) or update the table.

The following example maps the class-id column to a TINYINT and specifies new class-id values that will fit into a byte field:

Copy Code
<mapping>
 
<namespace name="Inheritance.Model">
   
<class name="Cat" >
     
<extension key="db-class-id" value="12" />
   
</class>
   
<class name="Dog">
     
<extension key="db-class-id" value="4" />
   
</class>
   
<class name="Pet">
     
<extension key="db-class-id" value="8">
       
<extension key="db-column">
         
<extension key="db-type" value="TINYINT" />
       
</extension>
     
</extension>
   
</class>
   
<class name="Rottweiler">
     
<extension key="db-class-id" value="6" />
   
</class>
   
<class name="WienerDog">
     
<extension key="db-class-id" value="2" />
   
</class>
 
</namespace>
</
mapping>

Mapping Defaults

By default the mapping is flat and a discriminator column is created.

In the current release the default mapping must be changed for every class. In future releases however, it would be possible to globally override this default.