Hibernate Inheritance Mapping
Do not miss this exclusive book on Binary Tree Problems. Get it now for free.
Hibernate is an implementation of JPA specification. Hibernate effectively takes care of the fundamental difference between how OOPs and RDBMS deal with data and provides ease of programming. Implementing various strategies for mapping hibernate deals with various Object-Relational Impedance mismatches.
In this article, we are going to learn various strategies of inheritance mapping that hibernate implements to deal with Inheritance Object-Relational Impedance Mismatch.
What is mapping
In simple terms, the mapping defines how an entity class of Object-Oriented paradigm will look like in a relational database table i.e. mapping is nothing but a translation of the Object model into the Data model.
In general Entity classes are mapped to tables, member variables of classes are mapped to columns of the tables and every object of the entity class corresponds to the record in the database table.
Consider the below entity class User
@Entity
@Table(name = "user")
public class User {
@Id
@Column(name = "id")
private int id;
@Column(name = "userName")
private String name;
@Column(name = "userEmail")
private String email;
//constructor, getters, and setters
}
The above class corresponds to the below table:
What is inheritance mapping
Before jumping into details of inheritance mapping let us first discuss, why we need inheritance mapping.
One of the important functionalities of the ORM tool is dealing with Object-Relational Impedance Mismatch. Now you would have thought what Object-Relational Impedance Mismatch is?
In the example above mapping of Java Object into a database table is not as easy as it seems. The above example is the simplest case. But there are many cases when mapping the Object model to the Data model is not that easy due to the perception and style used to deal with data in OOPs and RDBMS paradigms. This mismatch of perception, style, and paradigm is called "Object-Relational Impedance Mismatch".
Let us talk about some of the paradigm mismatches:
- Granularity: The number of classes in the Object model is not equal to the number of tables in the Data model
- Inheritance or Subtype: Inheritance is a feature of the Object-Oriented programming paradigm which is not available in RDBMS. There is no such concept of a table extending some another table.
- Association: In OOPs one class can contain a reference to another class which is called association but in RDBMS two tables are associated with foreign keys. There is no concept of a table into another table.
- Identity: Java provides "==" or equals() method to compare two objects but in RDBMS primary key is used to compare records.
- Data Navigation: In Java, we use the dot(.) operator to traverse through the network of objects but in RDBMS joins are required to retrieve records among tables.
Inheritance mapping efficiently deals with the Inheritance or Subtype Object-Relational Impedance Mismatch described above.
Now, when we have a little background on mapping and the need for inheritance mapping let us dig deeper into inheritance mapping. There's no straightforward way exists to map class hierarchies into database tables.
There are mainly three most used strategies to map class hierarchies into database tables:
- Table Per Class
- Table Per Subclass
- Table Per Hierarchy
Let us talk about each one in detail.
Table Per Concrete Class
In this mapping strategy, tables are created for each concrete class in the class hierarchy. If the superclass is an interface or an abstract class, no table will be created for it and if the superclass, too, is a concrete class then a table will be created for that too.
Let us consider the following object model diagram:
Here's what classes looks like to implement table per class inheritance mapping:
Person.java
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Person {
@Id
private Integer id;
private String name;
//constructor, getters, and setters
}
Note: At the id attribute we cannot put generation strategy while using table per concrete class strategy otherwise it throughs "MappingException".
Here's the Employee class that extends the Person class. The Employee class is a concrete class.
Employee.java
@Entity
public class Employee extends Person {
private String organization;
private Double salary;
//constructor, getters, and setters
}
Here is the concrete class Employee that extends Person class.
@Entity
public class Student extends Person {
private String instituteName;
private String course;
//constructor, getters, and setters
}
We only need to put the "@Inheritance" annotaion over the parent class. There is no need of putting it over child classes.
Here is the sql queries run by the hibernate to create the tables employee and student.
create table Employee (
id integer not null,
name varchar(255),
organization varchar(255),
salary float(53),
primary key (id)
);
create table Student (
id integer not null,
name varchar(255),
course varchar(255),
instituteName varchar(255),
primary key (id)
);
In the above queries we can clearly see that the id and name attributes present in the superclass, Person, has been replicated to the data model of the both subclasses, employee and student.
While using this strategy when we perform polymorphic queries to the database, behind the scenes the hibernate uses sql unions to retrieve the data from the database.
Table Per Subclass
In this strategy every subclass is mapped to it's own table. Whether the superclass is an abstract or a concrete class, a table is created for this also.
The table of superclass has columns according to the attributes present in the super class.
The subclasses has columns according to the attributes present the their class and additionaly it has an "id" column that is used to join this class with the superclass.
Here's what our Entity classes look like with table per subclass strategy:
Person.java
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {
@Id
private Integer id;
private String name;
//constructor, getters, and setters
}
Here, the strategy we use is InheritanceType.JOINED.So, it is also known as "Joined table strategy".
Here's the Employee subclass that extends Person superclass.
Employee.java
@Entity
public class Employee extends Person {
private String organization;
private Double salary;
//constructor, getters, and setters
}
Here's the Student subclass that extends Person superclass.
Student.java
@Entity
public class Student extends Person {
private String instituteName;
private String course;
//constructor, getters, and setters
}
The hibernate creates three tables:
- the person table that has columns- id and name,
- the employee table that has columns- id, organization, and salary, and
- the student table that has columns- id, institutionname, and course
The "id" column is the only the only column that replicated to all the other tables.
While performing polymorphic queries to retrieve that data from the database, hibernate uses join operation to fetch the data.
Due to join operation the JOINED table strategy lack in performance.
Table per class hierarchy
In this strategy only one table is created for the whole class hierarchy of the classes.
For the previously mentioned class diagram the Entity classes looks as follows:
Person.java
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "identity", discriminatorType = DiscriminatorType.STRING)
public class Person {
@Id
private Integer id;
private String name;
//constructor, getters, and setters
}
Here's the Employee class that extends Person class.
Employee.java
@Entity
@DiscriminatorValue(value = "employee")
public class Employee extends Person {
private String organization;
private Double salary;
//constructor, getters, and setters
}
Here's the Student class that extends Person class.
Student.java
@Entity
@DiscriminatorValue(value = "student")
public class Student extends Person {
private String instituteName;
private String course;
//constructor, getters, and setters
}
We use "InheritanceType.SINGLE_TABLE" strategy to implement the table per class hierarchy inheritance mapping strategy that's why it is also called Single Table Strategy.
Since only one table is created for the whole class hierarchy, we need a way to differentiate, distinguish or discriminate the records to identify which class it belongs to. For this purpose @DiscriminationColumn annotation is used over superclass and @DiscriminationValue annotation is used at subclass level.
Note: If we don't specify the @DicriminationColumn and @DiscriminationValue annotations, by default a "DTYPE" column is created and the value of this column is the name of the class itself.
There's only one table called "person", on the name of root of the hierarchy, is created that contains following columns:
- identity
- id
- name
- organization
- salary
- course and
- institutename
Here "identity" column is the dicriminator column that is used to discriminate, differentiate or distinguish the records that belongs to different classes.
Single table strategy is the default inheritance mapping strategy. Columns of the tables can contain null values in this strategy. Performance wise it is the best strategy to implement, as it does not require any join or union operation while retrieving the data from the database because all the columns are present in the single table only.
That's it for this text.💖👻💖
Happy coding!👻🤩👻
Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.