Inheritance is one of the biggest mismatch between object oriented model and relational model(SQL based models) because object oriented paradigm supports both Is a(Inheritance) and Has a(Association) relationship. But relational models support only Has a relationship by joining two entities using the concept of Foreign Key.
Hibernate follow four different approaches to represent Inheritance. While neither of them fully support Is a relationship. They are only the way of representing inheritance using has a relationship. We start with the basic one.

  1. Table per concrete class with implicit polymorphism
    This approach says that exactly one table for each concrete(Non abstract) class. This is the simplest way to inherit because at SQL level nothing has to be done for inheritance. All the inherited properties are mapped to each table.
    Key Points:

    • In hibernate only the mapped entities take part in persistence query. There is no need of mapping (@Entity) for parent class. So the classes extends the abstract class needs to persist only. That is why this approach named as Table per Concrete class.
    • To embed the super class properties in to concrete class, hibernate provided an annotation @MappedSuperClass. But this annotation is specific to hibernate only so its not standardized and portable.

    Here is an example.
    This is the parent class with @MappedSuperClass annotation. There is no need to define this class in xml file(like mapping class=”com.jft.Employee”/>).

    package com.jft;
    import javax.persistence.*;
    
    @MappedSuperclass
    public abstract class Employee {
    
        @Column(name = "name")
        String name;
    
        @Column(name = "contact_number")
        String contactNumber;
    
        @Column(name = "dept_name")
        String deptName;
    
        public Employee() {
        }
        public String toString() {
            return "Employee{Name : " + this.name + ", Contact Number : " + this.contactNumber + ", Department : " + deptName + "}";
        }
    
    /* All getter setter*/
    .
    .
    .
    }
    

    Two class Manager extends this class.

    @Entity
    public class Manager extends Employee {
    
        @Id
        @GeneratedValue
        @Column(name = "id")
        private int id;
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
        @Column(name = "no_of_employee_surpervised")
        Integer noOfEmployeesSurpervised;
    
        @Column(name = "allowances")
        BigDecimal allowances;
    
        @Column(name = "basic_salary")
        BigDecimal basicSalary;
    
        public Manager() {
        }
    
    /* All getter setter*/
    .
    .
    .
    }
    
    • Here primary keys are defined separately for both classes and can not query using Employee class to retrive the data.
  2. Table per concrete class with unions
    JPA called this strategy : Table per class. JPA specified this optional so its implementation is vendor specific.
    Key Points:

    • All the super class properties are inherited by all Concrete class also including the primary key. In previous strategy, the primary keys are defined separately because the abstract class is not the persistent class for hibernate in that case(as we did not mention the @Entity annotation). But for now, the abstract class has @entity annotation, this avoids the duplicate mappings.
    • Here we are going to use a special annotation for inheritance : @Inheritance. This annotation takes an argument called strategy or say Inheritance Type. Here we are using TABLE_PER_CLASS.

      @Entity
      @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
      public abstract class Employee {
          @Id
          @GeneratedValue
          @Column(name = "id")
          private int id;
      .
      .
      }
      
    • Super class will not exist in SQL schema. ORM knows that there is a super class and it will fetch the record from tables by its specially designed query and map them to its Concrete class objects.
      Query for above example looks like this :

      select employee.id , employee.contact_number, employee.dept_name, employee.name,employee.allowances, employee.basic_salary, employee.no_of_employee_surpervised, employee.no_of_hours_worked as no_of_ho1_2_, employee.rs_per_hour as rs_per_h2_2_, employee.clazz_ as clazz_ from 
      ( 
      select id, contact_number, dept_name, name, allowances, basic_salary, no_of_employee_surpervised, null::int4 as no_of_hours_worked, null::numeric as rs_per_hour, 1 as clazz_ from Manager 
      union all 
      select id, contact_number, dept_name, name, null::numeric as allowances, null::numeric as basic_salary, nu
      ll::int4 as no_of_employee_surpervised, no_of_hours_worked, rs_per_hour, 2 as clazz_ from worker_bee
      ) employee
      
    • Query uses the union operator to union all records comes from tables created for concrete classes. In 3rd line, those properties not exist in Manager class but exist in Worker BEE are set to null like no_of_hours_worked and rs_per_hour. It is because SELECT statement within the UNION must have the same number of columns.
    • Also a extra column added clazz_ whose values are fixed : 1 for Manager and 2 for Worker Bee. This field is required by ORM to specify which record belongs to which Concrete class. For example if it is 1 then create a Manager class object else if it is 2 then Worker Bee
  3. Table per class hierarchy
    Concept is to create a single table for abstract and all concrete classes. This strategy is best in all in terms of performance because it does not contain any joins or union. But there is one restriction :

    • Properties of the sub class can not be Not Null. Fields must be defined Null because a single table is used. A record will represent the one object of subclass but that tuple also contain properties(columns) of all other sub classes. So for that record we need to put null in all those columns which belongs to other sub classes.

    Key Points:

    • Inheritance Type defined for this strategy is : SINGLE_TABLE. We have to mention this in super class. Now a single table will create for super class and its sub classes, no matter how many sub classes are there.
    • In earlier approaches, a separate table is created for all sub classes so we knew that a particular record is belongs to which subclass but here we have a single table. ORM define a field in table to store record type, it is called Discriminator. By default the discriminator column is DTYPE of String type but we can define custom column in super class.
    • For above example, the table created is seems like this :

      create table Employee (
              DTYPE varchar(31) not null,
              id int4 not null,
              contact_number varchar(255),
              dept_name varchar(255),
              name varchar(255),
              allowances numeric(19, 2),
              basic_salary numeric(19, 2),
              no_of_employee_surpervised int4,
              no_of_hours_worked int4,
              rs_per_hour numeric(19, 2),
              primary key (id)
          )
      
  4. Table per sub class
    This approach simply implement the IS A relationship of object with HAS A relationship of database. Unlike above approaches the abstract classes and interfaces are also store in database as entity along with sub classes. Super class bind with the subclass using the relational foreign key associations.
    Key Points:

    • Primary key in the Super class mapped entity is act as primary key for corresponding sub class entity and foreign key to the super class.
      table per sub class1
    • Hibernate define inheritance Type for this strategy is : JOINED. We have to define this on super class. It indicates that the tables of sub classes will joined using foreign key to super class table.
    • To query the database it uses a special way similar to Table per concrete class with unions. For example in Select query, to find out which record belongs to which table(subclass), uses a case statement which depends upon the primary keys of the tables. Firstly check if the key is from first sub class then check for second subclass. If key does not belong to any sub class then it is super class table record. The example of the select query is given below :

      select BD.BILLING_DETAILS_ID, BD.OWNER, BD.NUMER, BD.CREATED,
      CC.TYPE, ..., BA.BANK_SWIFT, ...
      case
      when CC.CREDIT_CARD_ID is not null then 1
      when BA.BANK_ACCOUNT_ID is not null then 2
      when BD.BILLING_DETAILS_ID is not null then 0
      end as TYPE
      from BILLING_DETAILS BD
      left join CREDIT_CARD CC on
      BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID
      left join BANK_ACCOUNT BA on
      BD.BILLING_DETAILS_ID = BA.BANK_ACCOUNT_ID
      
    • This approach has completely normalized structure because of foreign key associations. Although the queries become more complex when the number of sub class increases because of extreme number of joins in query.

Each approach has its pro’s & con’s. Like Table per class hierarchy is simple in terms of database but the schema is not normalized and we have restriction to have null constraint for sub classes. So we can use one of the strategy to map inheritance to table based on the requirement. Its totally depends on the logic we want to implement and what type of database structure needed.