Modeling Single Table Inheritance

Let's look at a specific example shown below. An online bookstore may be selling regular paper books and electronic books. Both share a number of common attributes placed in a common superclass "Book", but also have a number of specific attributes found only in each book distribution format:

As with many other ORM inheritance examples, this one is contrived and oversimplified. In a real bookstore it is likely that the same book is available in both formats, so mapping this inheritance hierarchy as a composition may be more appropriate, but let's stick with this example for simplicity sake.

As you see, a "BOOK" table contains attributes from superclass and all subclasses, plus it has the ID (primary key) and a "TYPE" column which will be used as a "discriminator colum" to determine which sublcass is stored in each row. Mapping of this inheritance hierarchy, just like mapping a single class, starts by creating a DbEntity for the "BOOK" table:

Now let's create a superclass "Book", mapping only the columns that should be in the superclass (you can do it by clicking on "Create ObjEntity" icon on the DbEntity toolbar, and then removing unneeded subclass attributes). The next step is to create two subclasses. For each one of them, create a new ObjEntity, then under "Inheritance" dropdown specify "Book" as a superclass. This automatically selects "Table/View" to be "BOOK".

While Book is an abstract class and we did not specify entity qualifier for it (we could if it wasn't abstract), PaperBook and EBook both require "Qualifier" field to be populated. Let's assume that "E" is a designator for e-books, and "P" - for the paper books, then respective qualifiers would look like this:

  • type = 'E'
  • type = 'P'

Unlike some other ORMs, Cayenne is really flexible about the semantics of discriminator columns. Qualifier can be any valid Cayenne expression, as long as it doesn't span any relationships (i.e. uses columns of the root table only). It doesn't have to be a single collumn, it can contain less or greater expressions. E.g. "price > 500", "type = 'E' and sizeMB < 1000", etc. Note however that if the expression is not settable (e.g. when using < or >), Cayenne won't be able to guess the right values on insert and the user application must provide those values explicitly.

Also note that Qualifiers are not inherited. When defining qualifiers for inheritance purposes keep in mind that a fetch qualifier built by Cayenne will include the qualifier of a root entity of a query and qualifiers of all its known subentities joined using "or" operator.

Finally switch to "Attributes" tab and in addition to the already shown inherited attributes, add entity-specific attributes. Note that Relationships can also be inherited, or subclass-specific (not shown in our example).