Inheritance is an important and useful concept in object-oriented programming. Inheritance promotes the reuse of the functionality of existing components and makes the resulting program (application) more modular, easily understandable, and extensible. This post gives a brief introduction to the concept of inheritance and explains how JDXA, the KISS ORM for Android, can easily handle the persistence of objects involved in inheritance relationships.
In programming terms, an inheritance relationship between two object classes is established when one class (a subclass), in addition to defining its own attributes and methods, “inherits” the attributes and methods of another class (a superclass). In the Java language terminology, a subclass extends a superclass. Semantically, one may say that an instance of a subclass is also an instance of its superclass. Moreover, a subclass may be a superclass of yet another class. This kind of inheritance relationship amongst different classes creates a class hierarchy. For example, one may have the following class hierarchy in the domain model of an application:
Person is the superclass with BaseEmployee and Intern as its subclasses.
BaseEmployee has two subclasses – PermEmployee and TempEmployee.
Diagrammatically, this class hierarchy may be depicted in the following way:
Based on the above class inheritance structure, one may say that an instance of Intern is also an instance of Person (i.e., an Intern is a Person). Similarly, an instance of PermEmployee is also an instance of BaseEmployee. One may even say that an instance of TempEmployee is also an instance of Person.
A relational database is a preferred repository for the domain model objects of an application. An Object Relational Mapper (ORM) is typically used to simplify the writing of programming code needed to accomplish the saving and retrieval of domain model objects using a relational database (e.g., SQLite on the Android platform).
However, an ORM solution has to be fairly sophisticated to be able to persist (save) and retrieve objects in a class hierarchy. For example, an ORM solution should provide a simple and flexible way to define the underlying database table structure necessary to store objects of the class hierarchy. It should also provide simple and flexible APIs to save and retrieve (query) those objects, which may even be involved in relationships with other objects. Furthermore, an ORM should also provide a mechanism to retrieve only the objects of a top-level class if so desired.
With respect to the above domain model classes, if a query is made for Person objects, the ORM should also retrieve all the qualifying objects of all the subclasses (Intern, BaseEmployee, PermEmployee, and TempEmployee) from the database. Similarly, if a query is made for BaseEmployee objects, the ORM should also retrieve the qualifying objects of the PermEmployee and TempEmployee classes. One should also be able to optionally retrieve only the Person objects that were originally saved as Person objects (and not saved as, for example, Intern objects).
As explained below, JDXA ORM provides simple and flexible mechanisms for defining the mapping for a domain model class hierarchy declaratively. JDXA also provides simple and flexible APIs for saving and querying objects of a class hierarchy.
Here are some high-level code snippets for defining the domain model classes, specifying the mapping specification, and using the JDXA ORM APIs.
In the domain model class definitions below, the code for accessor (getters/setters) methods has been omitted for brevity.
Here is the JDXA ORM mapping specification for the domain model classes defined above:
As you can see, the declarative ORM specification is very simple and succinct. Most of the mapping information is automatically derived by JDXA based on some reasonable conventions. For example, a table name is assumed to be the same as the class name unless it is explicitly overridden (as in the case of the class TempEmployee above).
By default, JDXA stores instances of a class hierarchy in different tables using horizontal partitioning, meaning instances of any particular class are stored in one particular table assigned for that class. JDXA avoids the need for runtime joins by creating a separate subclass table with all the relevant columns needed for that subclass. So, in the above example, the table Intern will have columns for all the attributes of the class Intern, including those attributes inherited from the class Person.
JDXA also supports the option to store the instances of a class hierarchy in an aggregated database table, where an additional discriminating column value is used to identify the class name for the data in a particular row.
By default, a query on a superclass also retrieves the qualifying objects of the subclasses (polymorphism). However, you may restrict the selection to only the superclass objects by using the flag JDXS.QFLAG_PROPER_EXTENT in the query or access method calls.
You may delete multiple objects in a class hierarchy with a single JDXA API call.
Here are some code snippets, with embedded comments, exemplifying the power of JDXA APIs in terms of handling objects in a class hierarchy:
In summary, inheritance is a very powerful and useful concept in object-oriented programming. Inheritance enables the reuse of code, leading to more modular and compact application design.
JDXA ORM easily and elegantly supports the persistence of objects involved in inheritance relationships with its simple mapping specification and intuitive APIs. The JDXA SDK ships with a detailed sample project demonstrating JDXA’s support of inheritance.
Learn more about JDXA ORM, including how to get a free trial version, by visiting Software Tree’s website at http://www.softwaretree.com.