Overview
Inheritance is a familiar and a very useful concept to any Java developer. There are three common ways of mapping it to a relational schema. In Cayenne we are calling them "single table", "vertical" and "horizontal". Other ORM frameworks may use different terms for those (e.g. in JPA "single table" is called "single table per class", "vertical" - "joined subclass", and "horizontal" - "table per concrete class", still they are referring to the same types of mapping). The picture below is a high-level representation of these three strategies.
As of this writing Cayenne does not support horizontal inheritance. It may in the future. |
We'll discuss them in general terms in this chapter and provide Cayenne-specific details in dedicated chapters. Here we should mention that ORM inheritance should not be overused and there is some performance penalty associated with it. Often composition is a better way to model a particular problem.
Types of Inheritance
Single Table Inheritance
One database table is used to map all the subclasses and the superclass. So Superclass, Subclass1 and Subclass2 are all mapped to a single table. Such table has to contain columns to store the attributes of an entire class hierarchy. One or more columns are used as "discriminator columns" that tell Cayenne what type of record is stored in a given row. Single table inheritance provides good select performance, however the storage of data is not optimized.
Vertical Inheritance
One database table is used to map superclass columns, additionally one table per subclass is joined via a 1..1 relationship with the superclass table. So "Superclass" will have its own table, and extra attributes found in Subclass1 and Subclass2 will be stored in two separate tables joined with the superclass table. Discriminator columns are required for the same reason as with Single Table Inheritance. Vertical inheritance optimizes the data storage and provides a view of data very close to that of the Java classes. However that comes at a cost of doing one or more joins in every query, which may not scale into deep and wide inheritance hierarchies.
Horizontal Inheritance
(Currently unsupported by Cayenne)
With horizontal inheritance a superclass is usually abstract, and each subclass is mapped to its own independent database table. In our example "Superclass" will not have a table, all attributes, including inherited, of Subclass1 and Subclass2 will be stored in separate tables. Discriminator column is not required. Select performance of the horizontal inheritance mapping is not very good, as a union or a separate query per subclass is used to get the data.
Comparing ORM Inheritance Types
Inheritance Type | Discriminator Column | Primary Key | Pros and Cons |
---|---|---|---|
Single Table | required | shared by superclasses and subclasses | good select performance (no joins). Storage is not optimized. |
Vertical | required | propagated from superclass to subclasses via a join | optimized data storage and a data view close to object oriented one. Adds (N - 1) joins to select, where N is the number of classes in the hierarchy |
Horizontal (unsupported) | not needed | independent in each table | selects normally require a UNION across tables |
Mapping Inheritance in Cayenne
Now that we discussed various types of inheritance, it should be noted that the type of inheritance is not specified explicitly. Instead Cayenne guesses it from the mapping. This allows for mixing multiple types in a single hierarchy. The following chapters will explain how the mapping is done in each case: